[
  {
    "path": ".actrc",
    "content": "--bind\n--artifact-server-path=./artifacts\n-P ubuntu-22.04=ghcr.io/catthehacker/ubuntu:act-22.04"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/-game--bug-report.md",
    "content": "---\nname: \"[GAME] Bug report\"\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Describe the bug**\n\nA clear and concise description of what the bug is.\n\n**Error message**\n\nA lamp error message from the logs, or the one on screen.\n\n**To Reproduce**\n\nSteps to reproduce the behaviour:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behaviour**\n\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\n\nIf applicable, add screenshots to help explain your problem.\n\n**System Info (please complete the following information):**\n - Linux Flavour: Ubuntu / Steam OS / Arch\n - Window Manager: i3 / Qtile\n - Lamp Version: 1.0.0\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: enhancement\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/actions/setup-os/action.yml",
    "content": "name: Setup Os\n\ndescription: Provisions OS deps\n\nruns:\n  using: \"composite\"\n\n  steps:\n\n    - name: Update System \n      shell: bash\n      run: |\n        sudo apt update -y -qq\n    #\n    # was getting this error when trying to build with libpugixml-dev\n    # [release-please/release-please]   🐳  docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/5] user= workdir=\n    #     | -- The C compiler identification is GNU 9.4.0\n    #     | -- The CXX compiler identification is GNU 9.4.0\n    #     | -- Detecting C compiler ABI info\n    #     | -- Detecting C compiler ABI info - done\n    #     | -- Check for working C compiler: /usr/bin/cc - skipped\n    #     | -- Detecting C compile features\n    #     | -- Detecting C compile features - done\n    #     | -- Detecting CXX compiler ABI info\n    #     | -- Detecting CXX compiler ABI info - done\n    #     | -- Check for working CXX compiler: /usr/bin/c++ - skipped\n    #     | -- Detecting CXX compile features\n    #     | -- Detecting CXX compile features - done\n    #     | -- Found PkgConfig: /usr/bin/pkg-config (found version \"0.29.1\") \n    #     | -- Checking for one of the modules 'glfw3'\n    #     | -- Found Vulkan: /usr/lib/x86_64-linux-gnu/libvulkan.so (found version \"131\")  missing components: glslc glslangValidator\n    #     | -- Found CURL: /usr/lib/x86_64-linux-gnu/libcurl.so (found version \"7.68.0\")  \n    #     | -- Configuring done (0.9s)\n    #     | CMake Error at CMakeLists.txt:72 (target_link_libraries):\n    #     |   Target \"Lampray\" links to:\n    #     | \n    #     |     pugixml::pugixml\n    #     | \n    #     |   but the target was not found.  Possible reasons include:\n    #     | \n    #     |     * There is a typo in the target name.\n    #     |     * A find_package call is missing for an IMPORTED target.\n    #     |     * An ALIAS target is missing.\n    #     | \n    #     | \n    #     | \n    #     | -- Generating done (0.0s)\n    #     | CMake Generate step failed.  Build files cannot be regenerated correctly.\n    #\n    # so on advice from : https://stackoverflow.com/a/68752679\n    # we install with vcpkg instead \n    - name: Install Dev Dependencies\n      shell: bash\n      run: |\n        sudo apt install  -y -qq \\\n          libsdl2-dev \\\n          libcurl4-openssl-dev \\\n          libpugixml-dev \\\n          pkg-config \\\n          g++ \\\n          gcc\n\n    - name: Install Runtime Dependencies\n      shell: bash\n      run: |\n        sudo apt install  -y -qq \\\n          zenity\n"
  },
  {
    "path": ".github/actions/setup-tooling/README.md",
    "content": "# setup-tooling\n\nInstalls ASDF with plugins\n\n## Requirements\n\n- a `.tool-versions` file in the root of the repo\n\n## Usage\n\n```yml\n  Release:\n\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: write\n      deployments: write\n\n    steps:\n\n      - uses: actions/checkout@v3\n\n      - name: Setup tooling\n        uses: ./.github/actions/setup-tooling\n        env:\n          # if you use an asdf plugin called \"something\" and its not in the shortname repo: https://github.com/asdf-vm/asdf-plugins\n          # then it gets listed here as `ASDF_PLUGIN_URL_<pluginname>: the plugin repo`\n          ASDF_PLUGIN_URL_something: https://get.thething/asdf-plugin\n```\n\n- installs asdf plugins for each item listed in your `.tools-version`\n- if one of your plugins isn't on the official asdf list, then provide the\n  install url by defining an ENVVAR of `ASDF_PLUGIN_URL_pluginname=url`\n\n### Custom ASDF Setup command\n\nNormally you'd have a `setup.sh` in the root of the repo. If it's somewhere else, \nthen specify with your own version of the setup command:\n\n```yml\n  Release:\n\n    runs-on: ubuntu-latest\n\n    steps:\n\n      - uses: actions/checkout@v3\n\n      - name: Setup tooling\n        uses: ./.github/actions/setup-tooling\n        with:\n          SetupCommand: ./your/path/to/your/asdf/setup.sh\n```\n"
  },
  {
    "path": ".github/actions/setup-tooling/action.yml",
    "content": "name: Setup Asdf\n\ndescription: provisions tooling\n\ninputs:\n  SetupCommand:\n    description: \"Command to run\"\n    required: false\n    default: ./setup.bash\n\nruns:\n  using: \"composite\"\n\n  steps:\n    - name: Ensure setup command available\n      shell: bash\n      env:\n        SETUP_COMMAND: ${{inputs.SetupCommand}}\n      run: |\n        if [ ! -f ${SETUP_COMMAND} ]; then\n          echo \"🛑 Missing action input SetupCommand.\"\n          exit 1\n        fi\n\n    - uses: actions/cache@v3\n      id: online-asdf-cache\n      if: ${{ !env.ACT }}\n      with:\n        path: ~/.asdf\n        key: ${{ runner.os }}-asdf-${{ hashFiles('**/.tool-versions') }}\n        restore-keys: |\n          ${{ runner.os }}-asdf-\n    #\n    # This step serves as an interop layer between the online and offline\n    # when online, we use github cache and this step will report if the cache was hit\n    # when offline, we use act and this step will report that the cache was not hit\n    - name: act cache interop\n      shell: bash\n      id: cache\n      run: |\n        if [ \"${{ env.ACT }}\" = \"true\" ]; then \n          echo \"OFFLINE: no cache\"\n          echo \"cache-hit=false\" >> $GITHUB_OUTPUT\n        elif [ \"${{ steps.online-asdf-cache.outputs.cache-hit }}\" = \"true\" ]; then \n          echo \"ONLINE: cache available\"\n          echo \"cache-hit=${{ steps.online-asdf-cache.outputs.cache-hit }}\" >> $GITHUB_OUTPUT\n        fi\n\n    - run: echo \"${{ github.action_path }}\" >> $GITHUB_PATH\n      if: steps.cache.outputs.cache-hit != 'true'\n      shell: bash\n\n    - name: asdf install\n      if: steps.cache.outputs.cache-hit != 'true'\n      shell: bash\n      env:\n        SETUP_COMMAND: ${{inputs.SetupCommand}}\n      run: ${SETUP_COMMAND}\n\n    - name: set asdf path\n      shell: bash\n      run: |\n        echo \"$HOME/.asdf/bin\" >> $GITHUB_PATH\n        echo \"$HOME/.asdf/shims\" >> $GITHUB_PATH\n"
  },
  {
    "path": ".github/workflows/pr-title.yml",
    "content": "name: Check PR title\n\non:\n  pull_request_target:\n    types:\n      - opened\n      - reopened\n      - edited\n      - synchronize\n\njobs:\n  LintPrTitle:\n    runs-on: ubuntu-latest\n    permissions:\n      statuses: write\n    steps:\n      - uses: aslafy-z/conventional-pr-title-action@v3\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "on:\n  push:\n    branches:\n      - master\n\npermissions:\n  contents: write\n  pull-requests: write\n\nname: Release Management\n\njobs:\n  ReleaseAutomation:\n    \n    runs-on: ubuntu-22.04\n\n    steps:\n\n      - uses: google-github-actions/release-please-action@v3\n        id: release-please\n        # skip during local actions testing\n        if: ${{ !env.ACT }}\n        with:\n          release-type: simple\n          extra-files: |\n            VERSION\n            Lampray/Filesystem/lampFS.h\n      #\n      # This step serves as an interop layer between the online and offline\n      # when online, we use release-please and further execution is gated by the releases_created output\n      # when offline, we skip release-please and set releases_created to true to test creating a release build\n      - name: Declare Release Created\n        id: release\n        env:\n            RELEASE_PLEASE_TAGNAME: ${{ steps.release-please.outputs.tag_name }}\n            RELEASE_PLEASE_RELEASES_CREATED: ${{ steps.release-please.outputs.releases_created }}\n        run: |\n          if [ \"${{ env.ACT }}\" = \"true\" ]; then\n            echo \"OFFLINE: release created\"\n            echo \"releases_created=true\" >> $GITHUB_OUTPUT\n            echo \"release_tag=$(git describe --tags --abbrev=1)\" >> $GITHUB_OUTPUT\n\n          elif [ \"${RELEASE_PLEASE_RELEASES_CREATED}\" = \"true\" ]; then\n            echo \"ONLINE: release created\"\n            echo \"releases_created=true\" >> $GITHUB_OUTPUT\n            echo \"release_tag=${RELEASE_PLEASE_TAGNAME}\" >> $GITHUB_OUTPUT\n\n          else \n            echo \"ONLINE: release not created\"\n\n          fi\n\n      - name: Checkout\n        if: ${{ steps.release.outputs.releases_created == 'true' }}\n        uses: actions/checkout@v3\n\n      - name: OS Deps\n        if: ${{ steps.release.outputs.releases_created == 'true' }}\n        uses: ./.github/actions/setup-os\n      \n      # Just let ASDF do its thing. It's far easier and more reliable than trying to do it ourselves.\n      - name: Tooling\n        if: ${{ steps.release.outputs.releases_created == 'true' }}\n        uses: ./.github/actions/setup-tooling\n        with:\n          SetupCommand: ./setup.sh\n      \n      - name: Build\n        if: ${{ steps.release.outputs.releases_created == 'true' }}\n        run: |\n          ./build.sh Debug\n\n      # only upload the release artifact if we're online and not testing\n      - name: Upload Release Artifact\n        if: ${{ steps.release.outputs.releases_created == 'true' }}\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          RELEASE_TAG: ${{ steps.release.outputs.release_tag }}\n        run: |\n          if [ \"${{ env.ACT }}\" = \"true\" ]; then\n            echo \"OFFLINE: skipping upload\"\n            cp -r ./build/Lamp ./build/Lampray-${RELEASE_TAG}\n          else\n            gh release upload ${RELEASE_TAG} ./build/Lampray#lamp-${RELEASE_TAG}\n          fi\n          \n"
  },
  {
    "path": ".gitignore",
    "content": "## OSX artifacts\n.DS_Store\n\n## Dear ImGui artifacts\nimgui.ini\n\n## General build artifacts\n*.o\n*.obj\n*.exe\nexamples/*/Debug/*\nexamples/*/Release/*\nexamples/*/x64/*\n\n## Visual Studio artifacts\n.vs\nipch\n*.opensdf\n*.log\n*.pdb\n*.ilk\n*.user\n*.sdf\n*.suo\n*.VC.db\n*.VC.VC.opendb\n\n## Commonly used CMake directories\nbuild*/\n\n## Xcode artifacts\nproject.xcworkspace\nxcuserdata\n\n## Emscripten artifacts\nexamples/*.o.tmp\nexamples/*.out.js\nexamples/*.out.wasm\nexamples/example_glfw_opengl3/web/*\nexamples/example_sdl2_opengl3/web/*\nexamples/example_emscripten_wgpu/web/*\n\n## JetBrains IDE artifacts\n.idea\ncmake-build-*\n\n## Unix executables from our example Makefiles\nexamples/example_glfw_metal/example_glfw_metal\nexamples/example_glfw_opengl2/example_glfw_opengl2\nexamples/example_glfw_opengl3/example_glfw_opengl3\nexamples/example_glut_opengl2/example_glut_opengl2\nexamples/example_null/example_null\nexamples/example_sdl2_metal/example_sdl2_metal\nexamples/example_sdl2_opengl2/example_sdl2_opengl2\nexamples/example_sdl2_opengl3/example_sdl2_opengl3\nexamples/example_sdl2_sdlrenderer/example_sdl2_sdlrenderer\n"
  },
  {
    "path": ".tool-versions",
    "content": "cmake 3.27.0\nninja 1.11.1\nact 0.2.52\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## [1.3.2](https://github.com/CHollingworth/Lampray/compare/v1.3.1...v1.3.2) (2023-12-18)\n\n\n### Bug Fixes\n\n* Hotfix. Fixed update detection. ([b61e276](https://github.com/CHollingworth/Lampray/commit/b61e27658b866d350cfcaa009e3e507d278adf44))\n\n## [1.3.1](https://github.com/CHollingworth/Lampray/compare/v1.3.0...v1.3.1) (2023-12-16)\n\n\n### Bug Fixes\n\n* Sepeators are able to be removed ([45faaad](https://github.com/CHollingworth/Lampray/commit/45faaad64562b394ec908f85f8f6996cba917428))\n\n## [1.3.0](https://github.com/CHollingworth/Lampray/compare/v1.2.0...v1.3.0) (2023-12-16)\n\n\n### Features\n\n* Add confirmation before deleting mods ([#89](https://github.com/CHollingworth/Lampray/issues/89)) ([ab3e358](https://github.com/CHollingworth/Lampray/commit/ab3e358db686f7b00db9b1e236c54432294c60d4))\n* Add confirmation before deleting mods ([#98](https://github.com/CHollingworth/Lampray/issues/98)) ([fd72f1a](https://github.com/CHollingworth/Lampray/commit/fd72f1a0bd065afc406f51503e7e863afecdd654))\n* Add context menu for moving mods to top or bottom ([#93](https://github.com/CHollingworth/Lampray/issues/93)) ([e5303d3](https://github.com/CHollingworth/Lampray/commit/e5303d3b55620b155d9e05ef9049257c12e2e6a4))\n* Add mod separators ([#94](https://github.com/CHollingworth/Lampray/issues/94)) ([79e8d55](https://github.com/CHollingworth/Lampray/commit/79e8d55c6855419ca1d018915041a62e0929ef8d))\n* Add quit button to the menu ([#99](https://github.com/CHollingworth/Lampray/issues/99)) ([62d778c](https://github.com/CHollingworth/Lampray/commit/62d778cd448f853240b134e7544e185e5f21da65))\n\n\n### Bug Fixes\n\n* Added proper credit to contributors, they do amazing work please give em love. ([9c30861](https://github.com/CHollingworth/Lampray/commit/9c308616b8b60b0b16ade8615450b76b5e82eedf))\n* BG3 now functions. (First Push of LHL) ([a7b1848](https://github.com/CHollingworth/Lampray/commit/a7b1848dbbabe1202ca33a460386956bf257e6e9))\n* Cyberpunk deployment will now deploy all files. ([188c44f](https://github.com/CHollingworth/Lampray/commit/188c44f0b75c0b2900bbad72964f478b606f8db5))\n* Fix installing REDMod into the /mods directory ([#100](https://github.com/CHollingworth/Lampray/issues/100)) ([0b20181](https://github.com/CHollingworth/Lampray/commit/0b201817682fadfeacc958300b63472d032688c5))\n\n## [1.2.0](https://github.com/CHollingworth/Lampray/compare/v1.1.0...v1.2.0) (2023-12-04)\n\n\n### Features\n\n* Add configuration for disabling automatic update check and add a a button for manual checks ([#82](https://github.com/CHollingworth/Lampray/issues/82)) ([5c17701](https://github.com/CHollingworth/Lampray/commit/5c177011040327899543c63894152af7ba96a7d6))\n* Add mod filename checks to prevent adding duplicate mods ([#86](https://github.com/CHollingworth/Lampray/issues/86)) ([a5dd76f](https://github.com/CHollingworth/Lampray/commit/a5dd76f8287173112753690e1ea5d589103d6437))\n* Added a hint to the search bar. ([a1f4236](https://github.com/CHollingworth/Lampray/commit/a1f4236f27a818e0b620f164432b211cd116b7ba))\n* added tabs to the customise menu, renamed class. ([c78f8a3](https://github.com/CHollingworth/Lampray/commit/c78f8a3ba15cfd678d029e992d6063450fbaab00))\n* Added the ability to drop .pak files directly into lampray. ([bc01e96](https://github.com/CHollingworth/Lampray/commit/bc01e96f6d272e9addbf624ac158e71074b4b3d8))\n* Configurable alternate button color ([#80](https://github.com/CHollingworth/Lampray/issues/80)) ([163338b](https://github.com/CHollingworth/Lampray/commit/163338b4d1b95bd28e466b6545eb83566b0346cb))\n* Enable hiding table columns ([#79](https://github.com/CHollingworth/Lampray/issues/79)) ([bc094b8](https://github.com/CHollingworth/Lampray/commit/bc094b8a1c5b645536f35e0f39fdbd4a31b62737))\n* Highlight mods on hover ([#83](https://github.com/CHollingworth/Lampray/issues/83)) ([61c7be6](https://github.com/CHollingworth/Lampray/commit/61c7be6df4c17a06b92cd7c970f3f10b9b25c6d8))\n* Lampray will now open on the previous game modified instead of BG3. ([8a67d37](https://github.com/CHollingworth/Lampray/commit/8a67d3748b241c5b687b3344f784202d65e4cf61))\n\n\n### Bug Fixes\n\n* a crash that can happen when deleting mods ([#85](https://github.com/CHollingworth/Lampray/issues/85)) ([b0f3c22](https://github.com/CHollingworth/Lampray/commit/b0f3c2214273ce76d4e56f119b01ab7f116ab287))\n* Add missing return statements in lampConfig init ([#77](https://github.com/CHollingworth/Lampray/issues/77)) ([ce14529](https://github.com/CHollingworth/Lampray/commit/ce1452949fb82171b20592336c69aac6d742cd82))\n* Cyberpunk will now deploy archive and archivexl files correctly. ([5b49bad](https://github.com/CHollingworth/Lampray/commit/5b49bad47a335266f26335abad8000d49dbbff1c))\n* Fixed inconsistent colors between init and reset ([#84](https://github.com/CHollingworth/Lampray/issues/84)) ([91fe13c](https://github.com/CHollingworth/Lampray/commit/91fe13c670dc36d2f1b2cb164676cb135f0c7b00))\n* Modified b589dab so the delete button is faded out instead of replaced. ([65c0c42](https://github.com/CHollingworth/Lampray/commit/65c0c4241a4906884b1fb1265b7bebf0d96da6a8))\n* Rar support dropped. ([f1a09eb](https://github.com/CHollingworth/Lampray/commit/f1a09ebb1fba9d6dd1cc38b8d86e5d474e3c3366))\n* Re-enable deleting mod files with delete button and hide button on enabled mods ([#81](https://github.com/CHollingworth/Lampray/issues/81)) ([b589dab](https://github.com/CHollingworth/Lampray/commit/b589dabfce242b44f6bf1c40fd45164f7a924308))\n* Removed update checks in occordance with [#78](https://github.com/CHollingworth/Lampray/issues/78) ([6b5a8f2](https://github.com/CHollingworth/Lampray/commit/6b5a8f233a00ee5ccfb2f77c4208a4d60d1279eb))\n* Resolve minor compile warnings ([#90](https://github.com/CHollingworth/Lampray/issues/90)) ([454ec02](https://github.com/CHollingworth/Lampray/commit/454ec02a8bf408809a843819748a47ca8a9cdc62))\n\n## [1.1.0](https://github.com/CHollingworth/Lampray/compare/v1.0.12...v1.1.0) (2023-11-18)\n\n\n### Features\n\n* Configurable font size/scale ([#72](https://github.com/CHollingworth/Lampray/issues/72)) ([b9594d8](https://github.com/CHollingworth/Lampray/commit/b9594d8bba595d616acbd98b1d076efcba669cc8))\n* Mod Drag and Drop Reordering ([#71](https://github.com/CHollingworth/Lampray/issues/71)) ([fd0be98](https://github.com/CHollingworth/Lampray/commit/fd0be9812af3be7f3c452392db6389728e8010a7))\n\n\n### Bug Fixes\n\n* [#43](https://github.com/CHollingworth/Lampray/issues/43) Levenshtein Distance function expected strings of identical lengths. ([51e6e86](https://github.com/CHollingworth/Lampray/commit/51e6e860c953041bd421cfc6862c7399d30df948))\n* Added License information for third-party code. ([bc66c8b](https://github.com/CHollingworth/Lampray/commit/bc66c8bfc0b20957e40831b29719988b2ab920dd))\n\n## [1.0.12](https://github.com/CHollingworth/Lamp/compare/v1.0.11...v1.0.12) (2023-11-18)\n\n\n### Bug Fixes\n\n* Lamp has been renamed to Lampray! Deployment has changed resulting in slower deployment times but hiegher accuracy in load order. ([8ca573a](https://github.com/CHollingworth/Lamp/commit/8ca573ad83dd3e3f41cf326cee2b607cf735ad78))\n\n## [1.0.11](https://github.com/CHollingworth/Lamp/compare/v1.0.10...v1.0.11) (2023-11-06)\n\n\n### Bug Fixes\n\n* Update lampFS.h ([#68](https://github.com/CHollingworth/Lamp/issues/68)) ([e942810](https://github.com/CHollingworth/Lamp/commit/e9428107c4ad7927278f1f80491dcdaec0b713a0))\n\n## [1.0.10](https://github.com/CHollingworth/Lamp/compare/v1.0.9...v1.0.10) (2023-11-06)\n\n\n### Bug Fixes\n\n* Update main.cpp ([#66](https://github.com/CHollingworth/Lamp/issues/66)) ([7224e80](https://github.com/CHollingworth/Lamp/commit/7224e809b0d93a4e2ab3d2f48a37e06f15df56fd))\n\n## [1.0.9](https://github.com/CHollingworth/Lamp/compare/v1.0.8...v1.0.9) (2023-11-06)\n\n\n### Features\n\n* 1.0.9 ([#64](https://github.com/CHollingworth/Lamp/issues/64)) ([2d0657b](https://github.com/CHollingworth/Lamp/commit/2d0657b5e3acfeca87945fb471402b277a25a620))\n* Merge 1.0.9 into master ([#51](https://github.com/CHollingworth/Lamp/issues/51)) ([105595f](https://github.com/CHollingworth/Lamp/commit/105595f6eee4e3a7c5e3bee2ea604e984d87fc06))\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.26)\nproject(Lampray)\n\nset(CMAKE_CXX_STANDARD 17)\n\n\noption(USE_XDG_DIRECTORY \"Use XDG directory to store data instead of the working directory\" ON)\n\nadd_executable(${PROJECT_NAME} main.cpp\n        third-party/imgui/imconfig.h\n        third-party/imgui/imgui.cpp\n        third-party/imgui/imgui.h\n        third-party/imgui/imgui_demo.cpp\n        third-party/imgui/imgui_draw.cpp\n        third-party/imgui/imgui_internal.h\n        third-party/imgui/imgui_tables.cpp\n        third-party/imgui/imgui_widgets.cpp\n        third-party/imgui/imstb_rectpack.h\n        third-party/imgui/imstb_textedit.h\n        third-party/imgui/imstb_truetype.h\n        third-party/json/json.hpp\n        game-data/gameControl.h\n        third-party/l4z/lz4.c\n        third-party/l4z/lz4.h\n        VERSION\n        Lampray/Base/lampBase.h\n        Lampray/Filesystem/lampFS.h\n        Lampray/Control/lampControl.h\n        Lampray/Control/lampControl.cpp\n        Lampray/Parse/lampParse.h\n        game-data/BG3/BG3.cpp\n        game-data/BG3/BG3.h\n        Lampray/Control/lampConfig.h\n        Lampray/Control/lampConfig.cpp\n        Lampray/Filesystem/lampExtract.cpp\n        Lampray/Filesystem/lampIO.cpp\n        Lampray/Menu/lampMenu.h\n        Lampray/Menu/lampMenu.cpp\n        Lampray/Filesystem/lampUpdate.cpp\n        Lampray/Control/lampGames.h\n        Lampray/Filesystem/lampShare.cpp\n        Lampray/Menu/lampCustomise.h\n        Lampray/Filesystem/lampTrack.cpp\n        game-data/C77/C77.cpp\n        game-data/C77/C77.h\n        third-party/imgui/imgui_impl_sdl2.cpp\n        third-party/imgui/imgui_impl_sdlrenderer2.cpp\n        game-data/LHL/LHL.cpp\n        game-data/LHL/LHL.h\n        Lampray/Lang/lampLang.h\n)\n\nif(USE_XDG_DIRECTORY) \n  target_compile_definitions(${PROJECT_NAME} PUBLIC USE_XDG)\nendif(USE_XDG_DIRECTORY)\n\n\n\nfind_package(PkgConfig REQUIRED)\n\ntarget_link_libraries(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/third-party/bit7z/lib/x64/libbit7z64.a )\ntarget_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/third-party/bit7z/include/include/bit7z )\n\ntarget_link_libraries(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/third-party/nfd/lib/libnfd.a )\ntarget_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/third-party/nfd/include/include/nfd.h )\n\n\n# Find the SDL2 package\nfind_package(SDL2 REQUIRED)\n\n# Include SDL2 headers\ninclude_directories(${SDL2_INCLUDE_DIRS})\ntarget_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARIES})\n\n#add_subdirectory(third-party/SDL2)\n#target_link_libraries(${PROJECT_NAME} SDL)\n\n#find_package(pugixml REQUIRED)\n#target_link_libraries(${PROJECT_NAME} pugixml::pugixml)\n\nadd_subdirectory(third-party/pugixml)\ntarget_link_libraries(${PROJECT_NAME} pugixml::pugixml)\n\nfind_package(CURL REQUIRED)\ninclude_directories(${CURL_INCLUDE_DIRS})\ntarget_link_libraries(${PROJECT_NAME} ${CURL_LIBRARIES})\n\n\ntarget_link_libraries(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/third-party/l4z/liblz4.so)\ntarget_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/third-party/l4z/)\n\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Lampray\n\n> Thanks for helping make Lampray better! Follow this guide to learn how to contribute to Lampray.\n\n## 1. Fork the Repository\n\nStart by forking this repository to your GitHub account using the \"Fork\" button at the top right.\n\n## 2. Clone the Repository\n\nClone your forked repository to your local machine.\n\n## 3. Create a New Branch\n\nCreate a new branch for your work with a descriptive name.\n\n## 4. Make Changes\n\nMake your desired changes or improvements to the codebase.\n\n## 5. Test\n\nEnsure that your changes work as expected and that they don't introduce any new issues.\n\n## 6. Commit\n\nCommit your changes with a clear and concise commit message.\n\n## 7. Push \n\nPush your changes to your forked repository.\n\n## 8. Create a Pull Request\n\n1. Go to the original repository and click on the \"New Pull Request\" button.\n2. Fill in the pull request title with the following format\n    1. for new features, `feat: your feature oneliner name`\n    2. for bugfixes or minor changes: `fix: your bugfix oneline name`\n    3. for other kinds, see https://www.conventionalcommits.org/en/v1.0.0/#summary\n3. Follow the pull request template and fill in detailed description of your changes.\n\n## 9.  Discuss and Review\n\n💬 Engage in discussions with the maintainers and the community about your pull request.\n\n🤔 Be prepared to make any necessary adjustments based on feedback.\n\n## 10. Merge\n\n🎉 Once your pull request is reviewed and approved, it will be merged into the main branch."
  },
  {
    "path": "Doxyfile",
    "content": "# Doxyfile 1.9.8\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n#\n# Note:\n#\n# Use doxygen to compare the used configuration file with the template\n# configuration file:\n# doxygen -x [configFile]\n# Use doxygen to compare the used configuration file with the template\n# configuration file without replacing the environment variables or CMake type\n# replacement variables:\n# doxygen -x_noenv [configFile]\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the configuration\n# file that follow. The default is UTF-8 which is also the encoding used for all\n# text before the first occurrence of this tag. Doxygen uses libiconv (or the\n# iconv built into libc) for the transcoding. See\n# https://www.gnu.org/software/libiconv/ for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = Lamp\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         = 1.0.8\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          = \"A linux Mod Manager for Multiple games\"\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           = /home/charles/Desktop/LampRE/Lamp/logo/LMP-256.png\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = docs\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096\n# sub-directories (in 2 levels) under the output directory of each output format\n# and will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to\n# control the number of sub-directories.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = YES\n\n# Controls the number of sub-directories that will be created when\n# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every\n# level increment doubles the number of directories, resulting in 4096\n# directories at level 8 which is the default and also the maximum value. The\n# sub-directories are organized in 2 levels, the first level always has a fixed\n# number of 16 directories.\n# Minimum value: 0, maximum value: 8, default value: 8.\n# This tag requires that the tag CREATE_SUBDIRS is set to YES.\n\nCREATE_SUBDIRS_LEVEL   = 8\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian,\n# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English\n# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek,\n# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with\n# English messages), Korean, Korean-en (Korean with English messages), Latvian,\n# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese,\n# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish,\n# Swedish, Turkish, Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line\n# such as\n# /***************\n# as being the beginning of a Javadoc-style comment \"banner\". If set to NO, the\n# Javadoc-style will behave just like regular comments and it will not be\n# interpreted by doxygen.\n# The default value is: NO.\n\nJAVADOC_BANNER         = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# By default Python docstrings are displayed as preformatted text and doxygen's\n# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the\n# doxygen's special commands can be used and the contents of the docstring\n# documentation blocks is shown as doxygen documentation.\n# The default value is: YES.\n\nPYTHON_DOCSTRING       = YES\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:^^\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". Note that you cannot put \\n's in the value part of an alias\n# to insert newlines (in the resulting output). You can put ^^ in the value part\n# of an alias to insert a newline as if a physical newline was in the original\n# file. When you need a literal { or } or , in the value part of an alias you\n# have to escape them by means of a backslash (\\), this can lead to conflicts\n# with the commands \\{ and \\} for these it is advised to use the version @{ and\n# @} or use a double escape (\\\\{ and \\\\})\n\nALIASES                =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice\n# sources only. Doxygen will then generate output that is more tailored for that\n# language. For instance, namespaces will be presented as modules, types will be\n# separated into more groups, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_SLICE  = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,\n# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice,\n# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:\n# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser\n# tries to guess whether the code is fixed or free formatted code, this is the\n# default for Fortran type files). For instance to make doxygen treat .inc files\n# as Fortran files (default is PHP), and .f files as C (default is Fortran),\n# use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen. When specifying no_extension you should add\n# * to the FILE_PATTERNS.\n#\n# Note see also the list of default file extension mappings.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See https://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up\n# to that level are automatically included in the table of contents, even if\n# they do not have an id attribute.\n# Note: This feature currently applies only to Markdown headings.\n# Minimum value: 0, maximum value: 99, default value: 5.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nTOC_INCLUDE_HEADINGS   = 5\n\n# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to\n# generate identifiers for the Markdown headings. Note: Every identifier is\n# unique.\n# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a\n# sequence number starting at 0 and GITHUB use the lower case version of title\n# with any whitespace replaced by '-' and punctuation characters removed.\n# The default value is: DOXYGEN.\n# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.\n\nMARKDOWN_ID_STYLE      = DOXYGEN\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use\n# during processing. When set to 0 doxygen will based this on the number of\n# cores available in the system. You can set it explicitly to a value larger\n# than 0 to get more control over the balance between CPU load and processing\n# speed. At this moment only the input processing can be done using multiple\n# threads. Since this is still an experimental feature the default is set to 1,\n# which effectively disables parallel processing. Please report any issues you\n# encounter. Generating dot graphs in parallel is controlled by the\n# DOT_NUM_THREADS setting.\n# Minimum value: 0, maximum value: 32, default value: 1.\n\nNUM_PROC_THREADS       = 1\n\n# If the TIMESTAMP tag is set different from NO then each generated page will\n# contain the date or date and time when the page was generated. Setting this to\n# NO can help when comparing the output of multiple runs.\n# Possible values are: YES, NO, DATETIME and DATE.\n# The default value is: NO.\n\nTIMESTAMP              = NO\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual\n# methods of a class will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIV_VIRTUAL   = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = NO\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If this flag is set to YES, the name of an unnamed parameter in a declaration\n# will be determined by the corresponding definition. By default unnamed\n# parameters remain unnamed in the output.\n# The default value is: YES.\n\nRESOLVE_UNNAMED_PARAMS = YES\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# will also hide undocumented C++ concepts if enabled. This option has no effect\n# if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# declarations. If set to NO, these declarations will be included in the\n# documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# With the correct setting of option CASE_SENSE_NAMES doxygen will better be\n# able to match the capabilities of the underlying filesystem. In case the\n# filesystem is case sensitive (i.e. it supports files in the same directory\n# whose names only differ in casing), the option must be set to YES to properly\n# deal with such files in case they appear in the input. For filesystems that\n# are not case sensitive the option should be set to NO to properly deal with\n# output files written for symbols that only differ in casing, such as for two\n# classes, one named CLASS and the other named Class, and to also support\n# references to files without having to specify the exact matching casing. On\n# Windows (including Cygwin) and MacOS, users should typically set this option\n# to NO, whereas on Linux or other Unix flavors it should typically be set to\n# YES.\n# Possible values are: SYSTEM, NO and YES.\n# The default value is: SYSTEM.\n\nCASE_SENSE_NAMES       = SYSTEM\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class\n# will show which file needs to be included to use the class.\n# The default value is: YES.\n\nSHOW_HEADERFILE        = YES\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file. See also section \"Changing the\n# layout of pages\" for information.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as documenting some parameters in\n# a documented function twice, or documenting parameters that don't exist or\n# using markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete\n# function parameter documentation. If set to NO, doxygen will accept that some\n# parameters have no documentation without warning.\n# The default value is: YES.\n\nWARN_IF_INCOMPLETE_DOC = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong parameter\n# documentation, but not about the absence of documentation. If EXTRACT_ALL is\n# set to YES then this flag will automatically be disabled. See also\n# WARN_IF_INCOMPLETE_DOC\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about\n# undocumented enumeration values. If set to NO, doxygen will accept\n# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: NO.\n\nWARN_IF_UNDOC_ENUM_VAL = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS\n# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but\n# at the end of the doxygen process doxygen will return with a non-zero status.\n# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves\n# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not\n# write the warning messages in between other messages but write them at the end\n# of a run, in case a WARN_LOGFILE is defined the warning messages will be\n# besides being in the defined file also be shown at the end of a run, unless\n# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case\n# the behavior will remain as with the setting FAIL_ON_WARNINGS.\n# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# See also: WARN_LINE_FORMAT\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# In the $text part of the WARN_FORMAT command it is possible that a reference\n# to a more specific place is given. To make it easier to jump to this place\n# (outside of doxygen) the user can define a custom \"cut\" / \"paste\" string.\n# Example:\n# WARN_LINE_FORMAT = \"'vi $file +$line'\"\n# See also: WARN_FORMAT\n# The default value is: at line $line of file $file.\n\nWARN_LINE_FORMAT       = \"at line $line of file $file\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr). In case the file specified cannot be opened for writing the\n# warning and error messages are written to standard error. When as file - is\n# specified the warning and error messages are written to standard output\n# (stdout).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = Lamp\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see:\n# https://www.gnu.org/software/libiconv/) for the list of possible encodings.\n# See also: INPUT_FILE_ENCODING\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify\n# character encoding on a per file pattern basis. Doxygen will compare the file\n# name with each pattern and apply the encoding instead of the default\n# INPUT_ENCODING) if there is a match. The character encodings are a list of the\n# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding\n# \"INPUT_ENCODING\" for further information on supported encodings.\n\nINPUT_FILE_ENCODING    =\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by doxygen.\n#\n# Note the list of default checked file patterns might differ from the list of\n# default file extension mappings.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,\n# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl,\n# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php,\n# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be\n# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,\n# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.\n\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.cxx \\\n                         *.cxxm \\\n                         *.cpp \\\n                         *.cppm \\\n                         *.c++ \\\n                         *.c++m \\\n                         *.java \\\n                         *.ii \\\n                         *.ixx \\\n                         *.ipp \\\n                         *.i++ \\\n                         *.inl \\\n                         *.idl \\\n                         *.ddl \\\n                         *.odl \\\n                         *.h \\\n                         *.hh \\\n                         *.hxx \\\n                         *.hpp \\\n                         *.h++ \\\n                         *.ixx \\\n                         *.l \\\n                         *.cs \\\n                         *.d \\\n                         *.php \\\n                         *.php4 \\\n                         *.php5 \\\n                         *.phtml \\\n                         *.inc \\\n                         *.m \\\n                         *.markdown \\\n                         *.md \\\n                         *.mm \\\n                         *.dox \\\n                         *.py \\\n                         *.pyw \\\n                         *.f90 \\\n                         *.f95 \\\n                         *.f03 \\\n                         *.f08 \\\n                         *.f18 \\\n                         *.f \\\n                         *.for \\\n                         *.vhd \\\n                         *.vhdl \\\n                         *.ucf \\\n                         *.qsf \\\n                         *.ice\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# ANamespace::AClass, ANamespace::*Test\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       = *\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that doxygen will use the data processed and written to standard output\n# for further processing, therefore nothing else, like debug statements or used\n# commands (so in case of a Windows batch file always use @echo OFF), should be\n# written to standard output.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n# The Fortran standard specifies that for fixed formatted Fortran code all\n# characters from position 72 are to be considered as comment. A common\n# extension is to allow longer lines before the automatic comment starts. The\n# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can\n# be processed before the automatic comment starts.\n# Minimum value: 7, maximum value: 10000, default value: 72.\n\nFORTRAN_COMMENT_AFTER  = 72\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = YES\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# entity all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see https://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the\n# clang parser (see:\n# http://clang.llvm.org/) for more accurate parsing at the cost of reduced\n# performance. This can be particularly helpful with template rich C++ code for\n# which doxygen's built-in parser lacks the necessary type information.\n# Note: The availability of this option depends on whether or not doxygen was\n# generated with the -Duse_libclang=ON option for CMake.\n# The default value is: NO.\n\nCLANG_ASSISTED_PARSING = NO\n\n# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS\n# tag is set to YES then doxygen will add the directory of each input to the\n# include path.\n# The default value is: YES.\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\n\nCLANG_ADD_INC_PATHS    = YES\n\n# If clang assisted parsing is enabled you can provide the compiler with command\n# line options that you would normally use when invoking the compiler. Note that\n# the include paths will already be set by doxygen for the files and directories\n# specified with INPUT and INCLUDE_PATH.\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\n\nCLANG_OPTIONS          =\n\n# If clang assisted parsing is enabled you can provide the clang parser with the\n# path to the directory containing a file called compile_commands.json. This\n# file is the compilation database (see:\n# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the\n# options used when the source files were built. This is equivalent to\n# specifying the -p option to a clang tool, such as clang-check. These options\n# will then be passed to the parser. Any options specified with CLANG_OPTIONS\n# will be added as well.\n# Note: The availability of this option depends on whether or not doxygen was\n# generated with the -Duse_libclang=ON option for CMake.\n\nCLANG_DATABASE_PATH    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)\n# that should be ignored while generating the index headers. The IGNORE_PREFIX\n# tag works for classes, function and member names. The entity will be placed in\n# the alphabetical list under the first letter of the entity name that remains\n# after removing the prefix.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# Note: Since the styling of scrollbars can currently not be overruled in\n# Webkit/Chromium, the styling will be left out of the default doxygen.css if\n# one or more extra stylesheets have been specified. So if scrollbar\n# customization is desired it has to be added explicitly. For an example see the\n# documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output\n# should be rendered with a dark or light theme.\n# Possible values are: LIGHT always generate light mode output, DARK always\n# generate dark mode output, AUTO_LIGHT automatically set the mode according to\n# the user preference, use light mode if no preference is set (the default),\n# AUTO_DARK automatically set the mode according to the user preference, use\n# dark mode if no preference is set and TOGGLE allow to user to switch between\n# light and dark mode via a button.\n# The default value is: AUTO_LIGHT.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE        = AUTO_LIGHT\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a color-wheel, see\n# https://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use gray-scales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML\n# documentation will contain a main index with vertical navigation menus that\n# are dynamically created via JavaScript. If disabled, the navigation index will\n# consists of multiple levels of tabs that are statically embedded in every HTML\n# page. Disable this option to support browsers that do not have JavaScript,\n# like the Qt help browser.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_MENUS     = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be\n# dynamically folded and expanded in the generated HTML source code.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_CODE_FOLDING      = YES\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see:\n# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To\n# create a documentation set, doxygen will generate a Makefile in the HTML\n# output directory. Running make will produce the docset in that directory and\n# running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy\n# genXcode/_index.html for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag determines the URL of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDURL         =\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# on Windows. In the beginning of 2021 Microsoft took the original page, with\n# a.o. the download links, offline the HTML help workshop was already many years\n# in maintenance mode). You can download the HTML help workshop from the web\n# archives at Installation executable (see:\n# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo\n# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe).\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the main .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# The SITEMAP_URL tag is used to specify the full URL of the place where the\n# generated documentation will be placed on the server by the user during the\n# deployment of the documentation. The generated sitemap is called sitemap.xml\n# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL\n# is specified no sitemap is generated. For information about the sitemap\n# protocol see https://www.sitemaps.org\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSITEMAP_URL            =\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location (absolute path\n# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to\n# run qhelpgenerator on the generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine tune the look of the index (see \"Fine-tuning the output\"). As an\n# example, the default style sheet generated by doxygen has an example that\n# shows how to put an image at the root of the tree instead of the PROJECT_NAME.\n# Since the tree basically has the same information as the tab index, you could\n# consider setting DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the\n# FULL_SIDEBAR option determines if the side bar is limited to only the treeview\n# area (value NO) or if it should extend to the full height of the window (value\n# YES). Setting this to YES gives a layout similar to\n# https://docs.readthedocs.io with more room for contents, but less room for the\n# project logo, title, and description. If either GENERATE_TREEVIEW or\n# DISABLE_INDEX is set to NO, this option has no effect.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFULL_SIDEBAR           = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email\n# addresses.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nOBFUSCATE_EMAILS       = YES\n\n# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg\n# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see\n# https://inkscape.org) to generate formulas as SVG images instead of PNGs for\n# the HTML output. These images will generally look nicer at scaled resolutions.\n# Possible values are: png (the default) and svg (looks nicer but requires the\n# pdf2svg or inkscape tool).\n# The default value is: png.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FORMULA_FORMAT    = png\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# The FORMULA_MACROFILE can contain LaTeX \\newcommand and \\renewcommand commands\n# to create new LaTeX commands to be used in formulas as building blocks. See\n# the section \"Including formulas\" for details.\n\nFORMULA_MACROFILE      =\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# https://www.mathjax.org) which uses client side JavaScript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# With MATHJAX_VERSION it is possible to specify the MathJax version to be used.\n# Note that the different versions of MathJax have different requirements with\n# regards to the different settings, so it is possible that also other MathJax\n# settings have to be changed when switching between the different MathJax\n# versions.\n# Possible values are: MathJax_2 and MathJax_3.\n# The default value is: MathJax_2.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_VERSION        = MathJax_2\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. For more details about the output format see MathJax\n# version 2 (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3\n# (see:\n# http://docs.mathjax.org/en/latest/web/components/output.html).\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility. This is the name for Mathjax version 2, for MathJax version 3\n# this will be translated into chtml), NativeMML (i.e. MathML. Only supported\n# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This\n# is the name for Mathjax version 3, for MathJax version 2 this will be\n# translated into HTML-CSS) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from https://www.mathjax.org before deployment. The default value is:\n# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2\n# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        =\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# for MathJax version 2 (see\n# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# For example for MathJax version 3 (see\n# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):\n# MATHJAX_EXTENSIONS = ams\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see:\n# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using JavaScript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see:\n# https://xapian.org/). See the section \"External Indexing and Searching\" for\n# details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = NO\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when not enabling USE_PDFLATEX the default is latex when enabling\n# USE_PDFLATEX the default is pdflatex and when in the later case latex is\n# chosen this is overwritten by pdflatex. For specific output languages the\n# default can have been set differently, this depends on the implementation of\n# the output language.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         =\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# Note: This tag is used in the Makefile / make.bat.\n# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file\n# (.tex).\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to\n# generate index for LaTeX. In case there is no backslash (\\) as first character\n# it will be automatically added in the LaTeX code.\n# Note: This tag is used in the generated output file (.tex).\n# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.\n# The default value is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_MAKEINDEX_CMD    = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for\n# the generated LaTeX document. The header should contain everything until the\n# first chapter. If it is left blank doxygen will generate a standard header. It\n# is highly recommended to start with a default header using\n# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty\n# and then modify the file new_header.tex. See also section \"Doxygen usage\" for\n# information on how to generate the default header that doxygen normally uses.\n#\n# Note: Only use a user-defined header if you know what you are doing!\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. The following\n# commands have a special meaning inside the header (and footer): For a\n# description of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for\n# the generated LaTeX document. The footer should contain everything after the\n# last chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer. See also section \"Doxygen\n# usage\" for information on how to generate the default footer that doxygen\n# normally uses. Note: Only use a user-defined footer if you know what you are\n# doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as\n# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX\n# files. Set this option to YES, to get a higher quality PDF documentation.\n#\n# See also section LATEX_CMD_NAME for selecting the engine.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.\n# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch\n# mode nothing is printed on the terminal, errors are scrolled as if <return> is\n# hit at every error; missing files that TeX tries to input or request from\n# keyboard input (\\read on a not open input stream) cause the job to abort,\n# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,\n# but there is no possibility of user interaction just like in batch mode,\n# SCROLL In scroll mode, TeX will stop only for missing files to input or if\n# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at\n# each error, asking for user intervention.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# https://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)\n# path from which the emoji images will be read. If a relative path is entered,\n# it will be relative to the LATEX_OUTPUT directory. If left blank the\n# LATEX_OUTPUT directory will be used.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EMOJI_DIRECTORY  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's\n# configuration file, i.e. a series of assignments. You only have to provide\n# replacements, missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's configuration file. A template extensions file can be\n# generated using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = YES\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include\n# namespace members in file scope as well, matching the HTML output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_NS_MEMB_FILE_SCOPE = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures\n# the structure of the code including all documentation. Note that this feature\n# is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to Sqlite3 output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3\n# database with symbols found by doxygen stored in tables.\n# The default value is: NO.\n\nGENERATE_SQLITE3       = NO\n\n# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be\n# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put\n# in front of it.\n# The default directory is: sqlite3.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_OUTPUT         = sqlite3\n\n# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db\n# database file will be recreated with each doxygen run. If set to NO, doxygen\n# will warn if an a database file is already found and not modify it.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_SQLITE3 is set to YES.\n\nSQLITE3_RECREATE_DB    = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of\n# RECURSIVE has no effect here.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces\n# will be listed in the class and namespace index. If set to NO, only the\n# inherited external classes will be listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the topic index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to diagram generator tools\n#---------------------------------------------------------------------------\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of\n# subgraphs. When you want a differently looking font in the dot files that\n# doxygen generates you can specify fontname, fontcolor and fontsize attributes.\n# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,\n# Edge and Graph Attributes specification</a> You need to make sure dot is able\n# to find the font, which can be done by putting it in a standard location or by\n# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the\n# directory containing the font. Default graphviz fontsize is 14.\n# The default value is: fontname=Helvetica,fontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_COMMON_ATTR        = \"fontname=Helvetica,fontsize=10\"\n\n# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can\n# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a\n# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about\n# arrows shapes.</a>\n# The default value is: labelfontname=Helvetica,labelfontsize=10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_EDGE_ATTR          = \"labelfontname=Helvetica,labelfontsize=10\"\n\n# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes\n# around nodes set 'shape=plain' or 'shape=plaintext' <a\n# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>\n# The default value is: shape=box,height=0.2,width=0.4.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NODE_ATTR          = \"shape=box,height=0.2,width=0.4\"\n\n# You can set the path where dot can find font specified with fontname in\n# DOT_COMMON_ATTR and others dot attributes.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will\n# generate a graph for each documented class showing the direct and indirect\n# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and\n# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case\n# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the\n# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.\n# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance\n# relations will be shown as texts / links.\n# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.\n# The default value is: YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes. Explicit enabling a collaboration graph,\n# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the\n# command \\collaborationgraph. Disabling a collaboration graph can be\n# accomplished by means of the command \\hidecollaborationgraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies. Explicit enabling a group\n# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means\n# of the command \\groupgraph. Disabling a directory graph can be accomplished by\n# means of the command \\hidegroupgraph. See also the chapter Grouping in the\n# manual.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and\n# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS\n# tag is set to YES, doxygen will add type and arguments for attributes and\n# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen\n# will not generate fields with class member information in the UML graphs. The\n# class diagrams will look similar to the default class diagrams but using UML\n# notation for the relationships.\n# Possible values are: NO, YES and NONE.\n# The default value is: NO.\n# This tag requires that the tag UML_LOOK is set to YES.\n\nDOT_UML_DETAILS        = NO\n\n# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters\n# to display on a single line. If the actual line length exceeds this threshold\n# significantly it will wrapped across multiple lines. Some heuristics are apply\n# to avoid ugly line breaks.\n# Minimum value: 0, maximum value: 1000, default value: 17.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_WRAP_THRESHOLD     = 17\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,\n# can be accomplished by means of the command \\includegraph. Disabling an\n# include graph can be accomplished by means of the command \\hideincludegraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set\n# to NO, can be accomplished by means of the command \\includedbygraph. Disabling\n# an included by graph can be accomplished by means of the command\n# \\hideincludedbygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories. Explicit enabling a directory graph, when\n# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command\n# \\directorygraph. Disabling a directory graph can be accomplished by means of\n# the command \\hidedirectorygraph.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels\n# of child directories generated in directory dependency graphs by dot.\n# Minimum value: 1, maximum value: 25, default value: 1.\n# This tag requires that the tag DIRECTORY_GRAPH is set to YES.\n\nDIR_GRAPH_MAX_DEPTH    = 1\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# https://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file or to the filename of jar file\n# to be used. If left blank, it is assumed PlantUML is not used or called during\n# a preprocessing step. Doxygen will generate a warning when it encounters a\n# \\startuml command in this case and will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a\n# configuration file for plantuml.\n\nPLANTUML_CFG_FILE      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal\n# graphical representation for inheritance and collaboration diagrams is used.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate\n# files that are used to generate the various graphs.\n#\n# Note: This setting is not only used for dot files but also for msc temporary\n# files.\n# The default value is: YES.\n\nDOT_CLEANUP            = YES\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will\n# use a built-in version of mscgen tool to produce the charts. Alternatively,\n# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,\n# specifying prog as the value, doxygen will call the tool as prog -T\n# <outfile_format> -o <outputfile> <inputfile>. The external tool should support\n# output file formats \"png\", \"eps\", \"svg\", and \"ismap\".\n\nMSCGEN_TOOL            =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n"
  },
  {
    "path": "LICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n"
  },
  {
    "path": "Lampray/Base/lampBase.h",
    "content": "//\n// Created by charles on 27/09/23.\n//\n\n#ifndef LAMP_LAMPBASE_H\n#define LAMP_LAMPBASE_H\n\n#include <string>\n#include <pugixml.hpp>\n#include <utility>\n#include <vector>\n#include <sstream>\n#include \"../../third-party/imgui/imgui.h\"\n#include \"../Control/lampNotification.h\"\n#include <iomanip>\n#include <functional>\n#include <vector>\n#include <thread>\n#include <mutex>\n#include <condition_variable>\n#include <iostream>\n#include <filesystem>\n#include <cstring>\n#include <list>\n#include <queue>\n#include <fstream>\n\nnamespace Lamp::Core::Base{\n    /**\n     * @brief A holding class for some of the Lampray framework's core types.\n     *\n     */\n    class lampTypes{\n    public:\n        /**\n        * @brief A custom string class designed for use in the Lampray framework.\n        *\n        * This class provides custom functionality for string manipulation and conversion.\n        */\n        class lampString {\n        private:\n            std::string data; ///< The underlying std::string data.\n\n        public:\n            /**\n             * @brief Constructs a lampString from a std::string.\n             *\n             * @param str The std::string to initialize the lampString with.\n             */\n            lampString(std::string str) : data(std::move(str)) {}\n\n            /**\n             * @brief Constructs a lampString from a C-style string (const char*).\n             *\n             * @param str The C-style string to initialize the lampString with.\n             */\n            lampString(const char* str) : data(str) {}\n\n            /**\n             * @brief Constructs an empty lampString.\n             */\n            lampString() : data(\"\") {}\n\n            /**\n             * @brief Custom operator to concatenate a lampString with a C-style string (const char*).\n             *\n             * @param other The C-style string to concatenate with.\n             * @return A new lampString containing the concatenated result.\n             */\n            lampString operator+(const char* other) const {\n                return {data + other};\n            }\n\n            /**\n             * @brief Custom operator to concatenate two lampStrings.\n             *\n             * @param other The lampString to concatenate with.\n             * @return A new lampString containing the concatenated result.\n             */\n            lampString operator+(const lampString& other) const {\n                return {data + other.data};\n            }\n\n            /**\n             * @brief Custom operator to concatenate a std::string with a lampString.\n             *\n             * @param str1 The std::string to concatenate with.\n             * @param str2 The lampString to concatenate to the end.\n             * @return A new lampString containing the concatenated result.\n             */\n            friend lampString operator+(const std::string& str1, const lampString& str2) {\n                return {str1 + str2.data};\n            }\n\n            /**\n             * @brief Custom operator to concatenate a lampString with a std::string.\n             *\n             * @param other The std::string to concatenate with.\n             * @return A new lampString containing the concatenated result.\n             */\n            lampString operator+(const std::string& other) const {\n                return {data + other};\n            }\n\n            /**\n             * @brief Custom operator to assign a C-style string (const char*) to a lampString.\n             *\n             * @param other The C-style string to assign.\n             * @return A reference to the modified lampString.\n             */\n            lampString& operator=(const char* other) {\n                data = other;\n                return *this;\n            }\n\n            /**\n             * @brief Custom operator to assign a std::string to a lampString.\n             *\n             * @param other The std::string to assign.\n             * @return A reference to the modified lampString.\n             */\n            lampString& operator=(const std::string& other) {\n                data = other;\n                return *this;\n            }\n\n            /**\n             * @brief Custom comparison operator for lampStrings (less than).\n             *\n             * @param other The lampString to compare with.\n             * @return true if this lampString is less than the other, false otherwise.\n             */\n            bool operator<(const lampString& other) const {\n                return data < other.data;\n            }\n\n\n            /**\n             *  brief Custom equality operator for lampStrings\n             *\n             * @param other The lampString to compare with\n             * @return true if the contents of the strings are the same, false otherwise.\n             */\n            bool operator==(const lampString& other) const {\n              return data == other.data;\n            }\n\n            /**\n             * @brief Custom equality operator for C-style strings\n             *\n             * @param the string to compare to\n             * @return true if the contents of the strings are the same, fale otherwise.\n             */\n            bool operator==(const char* other) const {\n              return data == other;\n            }\n\n            /**\n             * @brief Custom operator to convert a lampString to a C-style string (const char*).\n             *\n             * @return A C-style string representing the content of the lampString.\n             */\n            operator const char*() const {\n                return data.c_str();\n            }\n\n            /**\n             * @brief Custom operator to convert a lampString to a std::string.\n             *\n             * @return A std::string representing the content of the lampString.\n             */\n            operator std::string() const {\n                return data;\n            }\n\n            /**\n             * @brief Custom operator to convert a lampString to a std::filesystem::path.\n             *\n             * @return A std::filesystem::path representing the content of the lampString.\n             */\n            operator std::filesystem::path() const {\n                return std::filesystem::path(data);\n            };\n\n            /**\n             * @brief Converts the lampString to a boolean value.\n             *\n             * This function interprets \"1\" or \"true\" as `true`, and anything else as `false`.\n             *\n             * @return true if the string is \"1\" or \"true,\" false otherwise.\n             */\n            bool as_bool() {\n                return (data == \"1\" || data == \"true\");\n            }\n\n            /**\n             * @brief Get C string equivalent\n             *\n             * @return  A pointer to the c-string representation of the lampString object's value.\n             */\n            const char *c_str() const {\n                return static_cast<const char*>(*this);\n            }\n        };\n\n        /**\n        * @brief A custom color class designed for use in the Lampray framework, representing a color with alpha (RGB-A).\n        *\n        * This class provides functionality to work with colors in RGBA format and supports conversion to/from hex strings.\n        */\n        class lampHexAlpha {\n        public:\n            /**\n             * @brief Constructs a lampHexAlpha with the default color (black with full alpha).\n             */\n            lampHexAlpha() : color_(0.0f, 0.0f, 0.0f, 1.0f) {}\n\n            /**\n             * @brief Constructs a lampHexAlpha from an ImVec4 color.\n             *\n             * @param color The ImVec4 color to initialize the lampHexAlpha with.\n             */\n            lampHexAlpha(const ImVec4& color) : color_(color) {}\n\n            /**\n             * @brief Constructs a lampHexAlpha from a hex color string.\n             *\n             * @param hexColor The hex color string (e.g., \"RRGGBB-AA\") to initialize the lampHexAlpha with.\n             */\n            lampHexAlpha(const std::string& hexColor) {\n                color_ = HexStringToImVec4(hexColor);\n            }\n\n            /**\n             * @brief Implicit conversion operator to ImVec4.\n             *\n             * @return An ImVec4 representing the color.\n             */\n            operator ImVec4() const {\n                return color_;\n            }\n\n            /**\n             * @brief Implicit conversion operator to ImU32 (ImGui color format).\n             *\n             * @return An ImU32 representing the color.\n             */\n            operator ImU32() const {\n                return ImGui::ColorConvertFloat4ToU32(color_);\n            }\n\n            /**\n             * @brief Implicit conversion operator to hex color string.\n             *\n             * @return A hex color string (e.g., \"RRGGBB-AA\").\n             */\n            operator std::string() const {\n                return ImVec4ToHexString(color_);\n            }\n\n        private:\n            ImVec4 color_; ///< The RGBA color represented by ImVec4.\n\n            /**\n             * @brief Converts an ImVec4 color to a hex color string.\n             *\n             * @param color The ImVec4 color to convert.\n             * @return A hex color string in the format \"RRGGBB-AA\".\n             */\n            std::string ImVec4ToHexString(const ImVec4& color) const {\n                int r = static_cast<int>(color.x * 255.0f);\n                int g = static_cast<int>(color.y * 255.0f);\n                int b = static_cast<int>(color.z * 255.0f);\n                int a = static_cast<int>(color.w * 255.0f);\n\n                std::stringstream stream;\n                stream << std::hex << std::setw(2) << std::setfill('0') << r\n                       << std::setw(2) << std::setfill('0') << g\n                       << std::setw(2) << std::setfill('0') << b\n                       << '-' << std::setw(2) << std::setfill('0') << a;\n\n                return stream.str();\n            }\n\n            /**\n             * @brief Converts a hex color string to an ImVec4 color.\n             *\n             * @param hexColor The hex color string in the format \"RRGGBB-AA\" to convert.\n             * @return An ImVec4 color.\n             */\n            ImVec4 HexStringToImVec4(const std::string& hexColor) const {\n                ImVec4 color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);\n                if (hexColor.size() >= 8) {\n                    // Split the hex string at the '-' delimiter\n                    std::string rgbHex = hexColor.substr(0, 6);\n                    std::string alphaHex = hexColor.substr(7, 2);\n\n                    // Convert RGB components from hex\n                    int r, g, b, a;\n                    std::istringstream(rgbHex.substr(0, 2)) >> std::hex >> r;\n                    std::istringstream(rgbHex.substr(2, 2)) >> std::hex >> g;\n                    std::istringstream(rgbHex.substr(4, 2)) >> std::hex >> b;\n                    std::istringstream(alphaHex) >> std::hex >> a;\n\n                    // Convert to ImVec4\n                    color.x = static_cast<float>(r) / 255.0f;\n                    color.y = static_cast<float>(g) / 255.0f;\n                    color.z = static_cast<float>(b) / 255.0f;\n                    color.w = static_cast<float>(a) / 255.0f;\n                }\n                return color;\n            }\n        };\n\n        /**\n        * @brief A custom class for representing return values with an associated reason.\n        *\n        * This class is designed for use in the Lampray framework to encapsulate return values along with an optional reason.\n        */\n        class lampReturn {\n        public:\n            int returnClause;      ///< The return clause indicating the outcome of an operation.\n            lampString returnReason = \"\"; ///< The optional return reason.\n\n            /**\n             * @brief Constructs a lampReturn with a return clause and an optional return reason.\n             *\n             * @param returnClause The return clause indicating the outcome of an operation.\n             * @param returnReason The optional return reason as a lampString.\n             */\n            lampReturn(int returnClause, lampString returnReason)\n                    : returnClause(returnClause), returnReason(returnReason) {}\n\n            /**\n             * @brief Constructs a lampReturn with a return clause and no return reason.\n             *\n             * @param returnClause The return clause indicating the outcome of an operation.\n             */\n            lampReturn(int returnClause) : returnClause(returnClause) {}\n\n            /**\n             * @brief Default constructor for a lampReturn with no return clause and no return reason.\n             */\n            lampReturn() {}\n\n            /**\n             * @brief Conversion operator to bool.\n             *\n             * @return true if the return clause is equal to 1, false otherwise.\n             */\n            operator bool() const {\n                return returnClause == 1;\n            }\n\n            /**\n             * @brief Conversion operator to int.\n             *\n             * @return The return clause as an integer.\n             */\n            operator int() const {\n                return returnClause;\n            }\n\n            /**\n             * @brief Conversion operator to lampString.\n             *\n             * @return The return reason as a lampString.\n             */\n            operator lampString() const {\n                return returnReason;\n            }\n\n            /**\n             * @brief Conversion operator to std::string.\n             *\n             * @return The return reason as a std::string.\n             */\n            operator std::string() const {\n                return static_cast<std::string>(returnReason);\n            }\n        };\n\n        /**\n        * @brief A structure representing identification information for game control.\n        *\n        * This structure is used to provide readable and shorthand names for game identification purposes.\n        */\n        struct lampIdent {\n            lampTypes::lampString ReadableName; ///< A human-readable name for the game.\n            lampTypes::lampString ShortHand;    ///< A shorthand or abbreviated name for the game.\n\n            /**\n             * @brief Constructs a lampIdent with a readable name and a shorthand name.\n             *\n             * @param readableName The human-readable name for the game.\n             * @param shortHand The shorthand or abbreviated name for the game.\n             */\n            lampIdent(lampTypes::lampString readableName, lampTypes::lampString shortHand)\n                    : ReadableName(readableName), ShortHand(shortHand) {}\n        };\n    };\n\n    /**\n    * @brief A class for managing game mods in the Lampray framework.\n    *\n    * This class provides functionality for managing and serializing game mods.\n    */\n    class lampMod {\n    public:\n        /**\n         * @brief The Mod struct represents information about a mod.\n         */\n        struct Mod {\n        public:\n            /**\n             * @brief The path to the mod's archive.\n             */\n            lampTypes::lampString ArchivePath;\n\n            /**\n             * @brief The type of the mod.\n             */\n            int modType;\n\n            /**\n             * @brief Indicates whether the mod is enabled.\n             */\n            bool enabled;\n\n            /**\n             * @brief Stores a string of the last time the file updated.\n             */\n            lampTypes::lampString timeOfUpdate = \"Unknown\";\n\n            /**\n             * @brief Serialize the Mod struct to an XML node.\n             *\n             * @param doc The parent XML node to which this Mod will be serialized.\n             * @return The serialized XML node.\n             */\n            pugi::xml_node serialize(pugi::xml_node doc) const {\n                pugi::xml_node modNode = doc.append_child(\"Mod\");\n\n                modNode.append_attribute(\"ArchivePath\").set_value(ArchivePath);\n                modNode.append_attribute(\"modType\").set_value(modType);\n                modNode.append_attribute(\"enabled\").set_value(enabled);\n                modNode.append_attribute(\"timeOfUpdate\").set_value(timeOfUpdate);\n\n                return modNode;\n            }\n\n            /**\n             * @brief Construct a new Mod object.\n             *\n             * @param archivePath The path to the mod's archive.\n             * @param modType The type of the mod.\n             * @param enabled Indicates whether the mod is enabled.\n             */\n            Mod(lampTypes::lampString archivePath, int modType, bool enabled)\n                    : ArchivePath(archivePath), modType(modType), enabled(enabled) {\n            }\n        };\n\n        /**\n         * @brief The Profile helper struct for managing profile-related operations.\n         */\n        class Profile {\n        public:\n            /**\n             * @brief Adds a value to an input string, separated by a tilde (~) delimiter.\n             *\n             * @param inputString The input string to which the value will be added.\n             * @param newValue The value to add to the input string.\n             */\n            static void addValue(std::string& inputString, const std::string& newValue) {\n                if (!inputString.empty()) {\n                    inputString += \"~\";\n                }\n                inputString += newValue;\n            }\n\n            /**\n             * @brief Removes a specified value from an input string.\n             *\n             * @param inputString The input string from which the value will be removed.\n             * @param valueToRemove The value to remove from the input string.\n             */\n            static void removeValue(std::string& inputString, const std::string& valueToRemove) {\n                size_t pos = inputString.find(valueToRemove);\n                if (pos != std::string::npos) {\n                    // Remove the value and the preceding tilde\n                    inputString.erase(pos - 1, valueToRemove.length() + 1);\n                }\n            }\n\n            /**\n             * @brief Splits a delimited input string into a vector of strings.\n             *\n             * @param inputString The input string to split.\n             * @return A vector of strings containing the split values.\n             */\n            static std::vector<std::string> splitString(const std::string& inputString) {\n                std::vector<std::string> values;\n                std::istringstream iss(inputString);\n                std::string token;\n\n                while (std::getline(iss, token, '~')) {\n                    values.push_back(token);\n                }\n\n                return values;\n            }\n        };\n    };\n\n    /**\n    * @brief A singleton class for logging messages and displaying warnings in the Lampray framework.\n    *\n    * This class provides a centralized logging system and a mechanism to display warning messages.\n    * It is implemented as a singleton to ensure a single instance throughout the application.\n    */\n    class lampLog {\n    public:\n        /**\n         * @brief Returns the singleton instance of the lampLog class.\n         *\n         * @return A reference to the singleton instance of the lampLog class.\n         */\n        static lampLog& getInstance()\n        {\n            static lampLog instance;\n            return instance;\n        }\n\n        lampLog(lampLog const&) = delete;\n        void operator=(lampLog const&) = delete;\n\n        /**\n         * @brief Enumeration representing different warning levels for log messages.\n         */\n        enum warningLevel {\n            LOG,     ///< Log message.\n            WARNING, ///< Warning message.\n            ERROR    ///< Error message.\n        };\n\n        /**\n         * @brief Enumeration representing error codes for log messages.\n         */\n        enum errorCode {\n            LMP_UNK = 0,           ///< Unknown error.\n            LMP_NODIRCREATION,     ///< Failed to create a directory.\n            LMP_NO7ZP,             ///< 7-Zip executable not found.\n            LMP_NOFILEDROP,        ///< File drop failed.\n            LMP_CLEANUPFAILED,     ///< Pre-cleanup failed.\n            LMP_PREDEPLOYFAILED,   ///< Pre-deployment tasks failed.\n            LMP_DEOPLYMENTFAILED,  ///< Deployment failed.\n            LMP_MODLISTSAVEFAILED, ///< Failed to save the mod list.\n            LMP_KEYSAVEFAILED,     ///< Failed to save a key.\n            LMP_KEYLOADFAILED,     ///< Failed to load a key.\n            LMP_EXTRACTIONFALED,   ///< Archive extraction failed.\n            LMP_NOCONFIG           ///< Configuration file not found.\n        };\n\n        std::list<std::string> poplist = {};\n        // Passthrough log.\n        lampTypes::lampReturn pLog(lampTypes::lampReturn data,warningLevel warningLevel = warningLevel::LOG,bool pop = false, errorCode errorCode = errorCode::LMP_UNK){\n            log(data.returnReason,warningLevel,pop,errorCode);\n            return data;\n        }\n\n        /**\n         * @brief Logs a message with the specified warning level and error code.\n         *\n         * @param data The message to log.\n         * @param warningLevel The warning level (LOG, WARNING, or ERROR).\n         * @param pop Indicates whether the message should be displayed as a pop-up.\n         * @param errorCode The error code associated with the message.\n         */\n        void log(lampTypes::lampString data, warningLevel warningLevel = warningLevel::LOG, bool pop = false, errorCode errorCode = errorCode::LMP_UNK) {\n            lampTypes::lampString ping;\n            switch (warningLevel) {\n                case LOG:\n                    ping = \"[LAMP:LOG:\" + std::to_string(errorCode) + \"] \" + data;\n                    break;\n                case WARNING:\n                    ping = \"[LAMP:WARNING:\" + std::to_string(errorCode) + \"] \" + data;\n                    break;\n                case ERROR:\n                    ping = \"[LAMP:ERROR:\" + std::to_string(errorCode) + \"] \" + data;\n                    break;\n            }\n            std::cout << ping << std::endl;\n\n            std::ofstream outputFile(\"lamp.log\", std::ios::app);\n            if (outputFile) {\n                std::string lineToAdd = \"This is a new line.\";\n                outputFile << ping << std::endl;\n                outputFile.close();\n            }\n\n            if (pop) {\n                switch (warningLevel) {\n                    case WARNING:\n                        Lamp::Core::lampNotification::getInstance().pushWarningNotification(ping);\n                        break;\n                    case ERROR:\n                        Lamp::Core::lampNotification::getInstance().pushErrorNotification(ping);\n                        break;\n                }\n                //  if(!lampConfig::getInstance().ShowIntroMenu) {\n                //poplist.push_front(ping);\n                //    }\n            }\n        }\n\n        /**\n         * @brief Displays warning pop-up messages from the poplist.\n         *\n         * @return true if there are warnings to display, false otherwise.\n         */\n        [[maybe_unused]] bool showWarns() {\n            for (auto it = poplist.begin(); it != poplist.end();) {\n                if (!poplist.empty()) {\n                    lampTypes::lampString x = *it;\n                    ImGuiIO &io = ImGui::GetIO();\n                    ImGui::SetNextWindowSize(io.DisplaySize, 0);\n                    ImGui::SetNextWindowPos(ImVec2(0, 0));\n\n                    ImGuiWindowFlags windowFlags = 0;\n                    windowFlags += ImGuiWindowFlags_NoMove;\n                    windowFlags += ImGuiWindowFlags_NoResize;\n                    windowFlags += ImGuiWindowFlags_NoCollapse;\n                    windowFlags += ImGuiWindowFlags_NoTitleBar;\n                    windowFlags += ImGuiWindowFlags_MenuBar;\n\n                    ImGui::Begin(x, NULL, windowFlags);\n\n                    ImGui::Text(\"%s\", x.c_str());\n                    ImGui::Separator();\n\n                    ImGui::Text(\"If an error persists please create an issue on GitHub.\");\n                    if (ImGui::Button(\"Okay\")) {\n                        poplist.erase(it);\n                    } else {\n                        ++it; // Move to the next element\n                    }\n\n                    ImGui::End();\n                }\n                break;\n            }\n            return true;\n        }\n\n    private:\n        /**\n         * @brief Private constructor for the lampLog class to enforce singleton pattern.\n         */\n        lampLog() {}\n    };\n\n    /**\n    * @brief A class for asynchronously executing code on a list of mods.\n    *\n    * The `lampExecutor` class allows you to execute a provided code function asynchronously on a list of mods.\n    * It keeps track of the total number of items and the count of finished items.\n    */\n    class lampExecutor {\n    public:\n        /**\n         * @brief Constructs a `lampExecutor` object with the provided code function.\n         *\n         * @param code The code function to execute on each mod.\n         */\n        lampExecutor(std::function<void(const lampMod::Mod *)> code) : code_(code), itemCount(0), finishedCount(0) {}\n\n        int itemCount;          ///< The total number of items to execute.\n        int finishedCount;      ///< The count of items that have finished execution.\n\n        /**\n         * @brief Executes the provided code function on a list of mods asynchronously.\n         *\n         * This method creates a separate thread for each mod in the list and executes the provided code function on it.\n         * It also keeps track of the number of finished executions.\n         *\n         * @param data The vector of mod pointers on which to execute the code.\n         */\n        void execute(const std::vector<lampMod::Mod *>& data, bool detach) {\n            lampLog::getInstance().log(\"ModList Executor: Starting\", lampLog::warningLevel::LOG);\n            itemCount = data.size();\n                for (const lampMod::Mod *item: data) {\n                    std::thread t([this, item]() {\n                        if (code_) {\n                            code_(item);\n                        }\n                        {\n                            std::unique_lock<std::mutex> lock(mutex_);\n                            finishedCount++;\n                        }\n                        condition_.notify_all();\n                    });\n                    if(detach) {\n                        t.detach();\n                    }else{\n                        t.join();\n                    }\n                }\n            waitForCompletion();\n        }\n\n        /**\n         * @brief Waits for the completion of all asynchronous executions.\n         *\n         * This method blocks until all asynchronous executions have finished.\n         */\n        void waitForCompletion() {\n            std::unique_lock<std::mutex> lock(mutex_);\n            condition_.wait(lock, [this]() {\n                return finishedCount == itemCount;\n            });\n        }\n\n    private:\n        std::function<void(const lampMod::Mod *)> code_;  ///< The code function to execute.\n        std::mutex mutex_;                                ///< Mutex for thread synchronization.\n        std::condition_variable condition_;               ///< Condition variable for thread synchronization.\n    };\n\n    /**\n    * @brief A utility class for sequencing and executing lambda expressions in named queues.\n    *\n    * The `LampSequencer` class allows you to add lambda expressions to named queues and execute them sequentially.\n    * It logs the results of the execution, including success, failure, and critical failure.\n    */\n    class LampSequencer {\n    public:\n        /**\n         * @brief Adds a lambda expression to the specified queue.\n         *\n         * @param queueName The name of the queue to which the lambda should be added.\n         * @param lambda The lambda expression to be added to the queue.\n         */\n        static void add(const std::string& queueName, std::function<lampTypes::lampReturn()> lambda) {\n            getQueue(queueName).push(lambda);\n        }\n\n        /**\n         * @brief Runs all lambda expressions in the specified queue sequentially.\n         *\n         * This method executes lambda expressions in the specified queue one by one and logs the results.\n         * Execution stops on critical failure (returnClause == -1) to prevent further execution.\n         *\n         * @param queueName The name of the queue to run.\n         * @return A lampReturn object indicating the overall result of the execution.\n         */\n        static lampTypes::lampReturn run(const std::string& queueName) {\n            lampLog::getInstance().log(\"Queue '\"+queueName+\"': Starting\", lampLog::warningLevel::LOG);\n            lampTypes::lampReturn result;\n            while (!getQueue(queueName).empty()) {\n                std::function<lampTypes::lampReturn()> lambda = getQueue(queueName).front();\n                getQueue(queueName).pop();\n                result = lambda();\n                logResult(queueName, result);\n                if (result.returnClause == -1 || result.returnClause == 0) {\n                    break;  // Stop execution on critical failure\n                }\n            }\n            return result;\n        }\n\n    private:\n        /**\n         * @brief Retrieves the queue associated with the specified name.\n         *\n         * This method returns the queue associated with the provided name or creates a new one if it doesn't exist.\n         *\n         * @param queueName The name of the queue to retrieve.\n         * @return A reference to the queue associated with the provided name.\n         */\n        static std::queue<std::function<lampTypes::lampReturn()>>& getQueue(const std::string& queueName) {\n            static std::unordered_map<std::string, std::queue<std::function<lampTypes::lampReturn()>>> queues;\n            return queues[queueName];\n        }\n\n        /**\n         * @brief Logs the result of lambda execution for the specified queue.\n         *\n         * This method logs the result of lambda execution in the specified queue, indicating success, failure, or critical failure.\n         *\n         * @param queueName The name of the queue.\n         * @param result The result of lambda execution.\n         */\n        static void logResult(const std::string& queueName, const lampTypes::lampReturn& result) {\n            std::string logMessage = \"Queue '\" + queueName + \"': \";\n            if (result.returnClause == 0) {\n                logMessage += \"Failed - \" + (std::string)result.returnReason;\n                lampLog::getInstance().log(logMessage, lampLog::warningLevel::ERROR, true);\n            } else if (result.returnClause == 1) {\n                logMessage += \"Success\";\n                lampLog::getInstance().log(logMessage, lampLog::warningLevel::LOG);\n            } else if (result.returnClause == -1) {\n                logMessage += \"Critically Failed - \" + (std::string)result.returnReason;\n                lampLog::getInstance().log(logMessage, lampLog::warningLevel::ERROR, true);\n            }\n        }\n    };\n\n    class OverlayBuilder{\n        std::vector<std::string> lowerPaths;\n        std::string configPath;\n        std::string gameTargetPath;\n\n        std::string removeCommand;\n        std::string buildCommand;\n    public:\n        lampTypes::lampReturn addPath(std::string path){\n            std::filesystem::path p(path);\n            if(std::filesystem::is_directory(p)){\n                lowerPaths.push_back(p);\n                return{1, \"Added path.\"};\n            }else{\n                return{0, \"Path is not a valid directory\"};\n            }\n\n        }\n\n        lampTypes::lampReturn create(std::string gameTarget, std::string configTarget, std::string workingTarget){\n                std::filesystem::path gamePath(gameTarget);\n                std::filesystem::path configPath(configTarget);\n                if(std::filesystem::exists(workingTarget) && std::filesystem::is_directory(workingTarget) && std::filesystem::exists(gamePath) && std::filesystem::is_directory(gamePath) && std::filesystem::exists(configPath) && std::filesystem::is_directory(configPath) ){\n                    // Rename the games folder and store a copy of the original name to be used for the merge.\n                    std::string managedString = std::string(\"Lampray Managed - \") + gamePath.filename().string();\n                    std::filesystem::path MergedPath = gamePath.parent_path() / managedString;\n                    std::filesystem::rename(gamePath, MergedPath);\n                    std::filesystem::create_directories(gamePath);\n\n                    // Build the command\n                    std::string command;\n                    command+= \"pkexec mount -t overlay overlay -o \";\n                    command+= \"lowerdir=\\\"\"+MergedPath.string()+\":\";\n                    // build the lowerdir\n                    if(!lowerPaths.empty()){\n                        command += lowerPaths[0];\n                        for(size_t i = 1; i < lowerPaths.size(); i++){\n                            command+=\":\"+lowerPaths[i];\n                        }\n                    }else{\n                        return{0, \"No mod paths have been added.\"};\n                    }\n                    command+=\"\\\",upperdir=\\\"\"+configTarget;\n                    command+=\"\\\",workdir=\\\"\"+workingTarget;\n                    command+=\"\\\" \\\"\"+gameTarget+\"\\\"\";\n                   // return {1, command};\n                    // Execute it\n\n                    int result = system(command.c_str());\n                    if(result == 0){\n                        return {1, \"Command successfully executed\"};\n                    }else{\n                        return {0, \"Command aborted\"};\n                    }\n                }else{\n                    return{0, \"Unable to create overlay, Targets Invalid.\"};\n                }\n        }\n\n    };\n}\n#endif //LAMP_LAMPBASE_H\n"
  },
  {
    "path": "Lampray/Control/lampConfig.cpp",
    "content": "//\n// Created by charles on 27/09/23.\n//\n#include \"lampConfig.h\"\n#include \"../Filesystem/lampFS.h\"\n\nbool Lamp::Core::lampConfig::init() {\n        Base::lampLog::getInstance().log(\"Initializing Lampray\");\n\n        if((std::string)bit7zLibraryLocation == \"\") {\n            Base::lampLog::getInstance().log(\"Searching for 7z.so\");\n            std::filesystem::path f{\"/usr/libexec/p7zip/7z.so\"};\n            if (std::filesystem::exists(f)) {\n                bit7zLibraryLocation = \"/usr/libexec/p7zip/7z.so\";\n            } else if (exists(std::filesystem::path{\"/usr/lib/p7zip/7z.so\"})) {\n                bit7zLibraryLocation = \"/usr/lib/p7zip/7z.so\";\n            } else if (exists(std::filesystem::path{\"/usr/lib64/p7zip/7z.so\"})) {\n                bit7zLibraryLocation = \"/usr/lib64/p7zip/7z.so\";\n            } else if (exists(std::filesystem::path{\"/usr/libexec/7z.so\"})) {\n                bit7zLibraryLocation = \"/usr/libexec/7z.so\";\n            } else {\n                Base::lampLog::getInstance().log(\"Fatal. Cannot locate 7z.so\", Base::lampLog::ERROR, true,\n                                                 Base::lampLog::LMP_NO7ZP);\n                return false;\n            }\n        }\n\n    return true;\n}\n\nImGuiWindowFlags Lamp::Core::lampConfig::DefaultWindowFlags() {\n    ImGuiWindowFlags windowFlags = 0;\n    windowFlags += ImGuiWindowFlags_NoMove;\n    windowFlags += ImGuiWindowFlags_NoResize;\n    windowFlags += ImGuiWindowFlags_NoCollapse;\n    windowFlags += ImGuiWindowFlags_NoTitleBar;\n    windowFlags += ImGuiWindowFlags_MenuBar;\n    return windowFlags;\n}\n\n"
  },
  {
    "path": "Lampray/Control/lampConfig.h",
    "content": "//\n// Created by charles on 27/09/23.\n//\n\n#ifndef LAMP_LAMPCONFIG_H\n#define LAMP_LAMPCONFIG_H\n\n#include <list>\n#include <filesystem>\n#include <variant>\n#include \"../../game-data/gameControl.h\"\n#include \"../Base/lampBase.h\"\n\nnamespace Lamp::Core {\n    typedef Core::Base::lampTypes::lampString lampString;\n    typedef Core::Base::lampTypes::lampHexAlpha lampHex;\n    typedef Core::Base::lampTypes::lampReturn lampReturn;\n\n/**\n * @brief The `lampConfig` class manages configuration settings and data paths for Lampray.\n *\n * The `lampConfig` class provides access to Lampray's configuration settings, data paths, and other\n * configuration-related functionality.\n */\n    class lampConfig {\n    public:\n        /**\n         * @brief Gets the singleton instance of the `lampConfig` class.\n         *\n         * @return The reference to the singleton instance.\n         */\n        static lampConfig& getInstance()\n        {\n            static lampConfig instance;\n            return instance;\n        }\n\n        lampConfig(lampConfig const&) = delete;\n        void operator=(lampConfig const&) = delete;\n    \n        const lampString baseDataPath = getBaseDataPath();\n        const lampString saveDataPath = baseDataPath + \"Mod_Lists/\";\n        const lampString archiveDataPath = baseDataPath + \"Archives/\";\n        const lampString ConfigDataPath = baseDataPath + \"Config/\";\n        const lampString DeploymentDataPath = baseDataPath + \"Deployment/\";\n        const lampString workingPaths = baseDataPath + \"WorkingDirectories/\";\n\n        lampString bit7zLibraryLocation = \"\";\n\n        const bool defaultCheckForUpdateAtStart = true;\n        bool checkForUpdatesAtStartup = true;\n\n        int listHighlight = -1;\n        char searchBuffer[250]{};\n\n        std::map<lampString, lampString> lampFlags = {\n                {\"showIntroMenu\", \"1\"},\n        };\n\n        /**\n         * @brief Gets the default ImGui window flags for Lampray windows.\n         *\n         * @return The ImGui window flags.\n         */\n        ImGuiWindowFlags DefaultWindowFlags();\n\n        /**\n         * @brief Initializes the `lampConfig` instance.\n         *\n         * @return `true` if initialization was successful; otherwise, `false`.\n         */\n        bool init();\n\n    private:\n        /**\n         * @brief Private constructor for the `lampConfig` class.\n         *\n         * The constructor is private to ensure that only one instance of `lampConfig` can exist.\n         */\n        lampConfig(){};\n\n        static lampString getBaseDataPath() {\n          std::string ret = \"\";\n#ifdef USE_XDG\n          char *xdg_data_home = std::getenv(\"XDG_DATA_HOME\");\n\n          if (!xdg_data_home) {\n            ret += std::getenv(\"HOME\"); \n            ret += \"/.local/share\";\n          }\n          \n          ret += \"/lampray/\";\n#else \n          ret = \"Lamp_Data/\";\n#endif\n          return lampString(ret);\n        }\n    };\n};\n#endif //LAMP_LAMPCONFIG_H\n"
  },
  {
    "path": "Lampray/Control/lampControl.cpp",
    "content": "//\n// Created by charles on 27/09/23.\n//\n#include \"lampControl.h\"\n\nLamp::Core::lampString Lamp::Core::lampControl::getFormattedTimeAndDate() {\n    std::time_t unixTimestamp = std::time(nullptr);\n    std::tm timeInfo;\n    localtime_r(&unixTimestamp, &timeInfo);\n    std::ostringstream oss;\n    oss << std::setfill('0');\n    oss << std::setw(2) << timeInfo.tm_mday << '/' << std::setw(2) << (timeInfo.tm_mon + 1) << '/'\n        << (timeInfo.tm_year + 1900) << ' ';\n    oss << std::setw(2) << timeInfo.tm_hour << ':' << std::setw(2) << timeInfo.tm_min;\n    return oss.str();\n}\n\n\n\nLamp::Core::lampControl::lampGameSettingsDisplayHelper::lampGameSettingsDisplayHelper(\n        std::string displayString, std::string stringTarget, std::string toolTip, std::string keyPath)\n        : displayString(displayString), stringTarget(stringTarget), toolTip(toolTip), keyPath(keyPath) {\n\n\n}\n"
  },
  {
    "path": "Lampray/Control/lampControl.h",
    "content": "//\n// Created by charles on 27/09/23.\n//\n\n#ifndef LAMP_LAMPCONTROL_H\n#define LAMP_LAMPCONTROL_H\n\n#include <list>\n#include <filesystem>\n#include \"../../game-data/gameControl.h\"\n#include \"../Base/lampBase.h\"\n#include \"../Filesystem/lampFS.h\"\n#include \"lampGames.h\"\n#include \"../../third-party/nfd/include/nfd.h\"\n#include \"../Lang/lampLang.h\"\n\nnamespace Lamp::Core{\n    typedef Core::Base::lampTypes::lampString lampString;\n    typedef Core::Base::lampTypes::lampHexAlpha lampHex;\n    typedef Core::Base::lampTypes::lampReturn lampReturn;\n    /**\n    * @brief The `lampControl` class manages Lampray's control and user interface.\n    *\n    * The `lampControl` class provides access to various control and user interface-related functionalities in Lampray.\n    */\n    class lampControl{\n    public:\n        /**\n         * @brief Gets the singleton instance of the `lampControl` class.\n         *\n         * @return The reference to the singleton instance.\n         */\n        static lampControl& getInstance()\n        {\n            static lampControl instance;\n            return instance;\n        }\n\n        lampControl(lampControl const&) = delete;\n        void operator=(lampControl const&)  = delete;\n\n        /**\n         * @brief Gets the current formatted time and date as a string.\n         *\n         * @return The formatted time and date string.\n         */\n        static lampString getFormattedTimeAndDate();\n\n        lampHex Colour_SearchHighlight = ImVec4(0.3f, 0.f, 0.3f, 1);\n        lampHex Colour_ButtonAlt = ImVec4(0.1f, 0.6f, 0.3f, 1);\n\n        // an index outside of the loop/object recreation that we can reference for the delete modal (otherwise modal options display for every mod in the ModList)\n        int deletePos = -1;\n        /**\n        * @brief The `lampArchiveDisplayHelper` struct provides helper methods for displaying archives.\n        */\n        struct lampArchiveDisplayHelper{\n        private:\n            std::list<std::string> columnNames{lampLang::LS(\"LAMPRAY_MODLIST_TITLE_ENABLED\"),lampLang::LS(\"LAMPRAY_MODLIST_TITLE_MODNAME\"), lampLang::LS(\"LAMPRAY_MODLIST_TITLE_MODTYPE\"), lampLang::LS(\"LAMPRAY_MODLIST_TITLE_ORDER\"),lampLang::LS(\"LAMPRAY_MODLIST_TITLE_LASTUPDATE\") ,lampLang::LS(\"LAMPRAY_MODLIST_TITLE_REMOVEMOD\")};\n            std::vector<Base::lampMod::Mod *>& ModList;\n            std::list<std::pair<std::string,bool *>> ExtraOptions;\n            std::string temp{\"0\"};\n\n            bool collapsUntilNextSeparator = false;\n            std::string modSeparatorDefaultText = \"====================================================\";\n\n            /**\n            * @brief Calculates the Levenshtein distance between two strings.\n            *\n            * @param s1 The first string.\n            * @param s2 The second string.\n            * @return The Levenshtein distance between the two strings.\n             */\n            int calculateLevenshteinDistance(const std::string& s1, const std::string& s2) {\n                int len1 = s1.length();\n                int len2 = s2.length();\n\n                std::vector<std::vector<int>> dp(len1 + 1, std::vector<int>(len2 + 1, 0));\n\n                for (int i = 1; i <= len1; ++i) {\n                    dp[i][0] = i;\n                }\n\n                for (int j = 1; j <= len2; ++j) {\n                    dp[0][j] = j;\n                }\n\n                for (int i = 1; i <= len1; ++i) {\n                    for (int j = 1; j <= len2; ++j) {\n                        int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;\n                        dp[i][j] = std::min({ dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost });\n                    }\n                }\n\n                return dp[len1][len2];\n            }\n\n\n            int findClosestMatchPosition() {\n\n                if(std::strlen(lampConfig::getInstance().searchBuffer) <= 1 ){\n                    return -1;\n                }\n\n                int closestMatchPosition = -1;\n                int minDistance = std::numeric_limits<int>::max();\n\n                for (auto it = ModList.begin(); it != ModList.end(); ++it) {\n                    std::filesystem::path path((*it)->ArchivePath);\n                    std::string cutname = path.filename().c_str();\n\n                    while (cutname.length() < 100) {\n                        cutname += '-';\n                    }\n\n                    int distance = calculateLevenshteinDistance(cutname, lampConfig::getInstance().searchBuffer);\n\n                    if (distance < minDistance) {\n                        minDistance = distance;\n                        closestMatchPosition = std::distance(ModList.begin(), it);\n                    }\n                }\n\n                return closestMatchPosition;\n            }\n\n            /**\n             * @brief Moves an item up in the mod list.\n             *\n             * @param it Iterator pointing to the item to move.\n             */\n            void moveUp(std::vector<Base::lampMod::Mod*>::iterator& it) {\n                if (ModList.size() <= 1 || it == ModList.begin()) {\n                    return; // Nothing to move or already at the beginning\n                }\n\n                std::swap(*it, *(it - 1));\n                --it;\n            }\n            /**\n             * @brief Moves an item down in the mod list.\n             *\n             * @param it Iterator pointing to the item to move.\n             */\n            void moveDown(std::vector<Base::lampMod::Mod*>::iterator& it) {\n                if (ModList.size() <= 1 || it == ModList.end() - 1) {\n                    return; // Nothing to move or already at the end\n                }\n\n                std::swap(*it, *(it + 1));\n                ++it;\n            }\n\n            /**\n             * @brief Moves an item to specific position in the mod list.\n             *\n             * @param it Iterator pointing to the item to move.\n             * @param position Integer position to move the item to.\n             */\n            void moveModTo(std::vector<Base::lampMod::Mod*>::iterator& it, int position) {\n                int currentPos = it - ModList.begin();\n                if(currentPos > position){\n                    for(int ind = currentPos; ind > position; ind--){\n                        moveUp(it);\n                    }\n                } else if(currentPos < position){\n                    for(int ind = currentPos; ind < position; ind++){\n                        moveDown(it);\n                    }\n                }\n            }\n\n        public:\n\n\n            /**\n             * @brief Constructs a `lampArchiveDisplayHelper` object.\n             *\n             * @param ExtraColumnNames Extra column names to be displayed.\n             * @param modList A vector of mod objects.\n             * @param extraOptions A list of extra options.\n             */\n            lampArchiveDisplayHelper(std::list<std::string> ExtraColumnNames, std::vector<Base::lampMod::Mod *> &modList,\n                    std::list<std::pair<std::string, bool * >> extraOptions)\n            : ModList(modList),\n            ExtraOptions(extraOptions) {\n                columnNames.insert(columnNames.end(), ExtraColumnNames.begin(), ExtraColumnNames.end());\n            }\n            /**\n         * @brief Creates an ImGui menu for displaying mod archives.\n         */\n            void createImguiMenu(){\n                ImGuiIO &io = ImGui::GetIO();\n                ImGui::SetNextItemWidth(io.DisplaySize.x);\n                if (ImGui::InputTextWithHint(\"##searcher\",lampLang::LS(\"LAMPRAY_MODLIST_SEARCH\"), lampConfig::getInstance().searchBuffer, 250)) {\n                    lampConfig::getInstance().listHighlight = findClosestMatchPosition();\n                }\n\n                ImGuiTableFlags mod_table_flags = 0;\n                mod_table_flags |= ImGuiTableFlags_SizingStretchProp;\n                mod_table_flags |= ImGuiTableFlags_Hideable; // allow hiding coumns via context menu\n                mod_table_flags |= ImGuiTableFlags_Reorderable; // allow reordering columns\n\n                if(ImGui::BeginTable(Lamp::Games::getInstance().currentGame->Ident().ShortHand, columnNames.size() + 1, mod_table_flags)) {\n                    for (auto it = columnNames.begin(); it != columnNames.end(); ++it) {\n                        ImGui::TableSetupColumn((*it).c_str());\n                    }\n\t\t\t\t\tImGui::TableHeadersRow();\n                    ImGui::TableNextRow();\n\n                    int i = 0;\n                    for (auto it = ModList.begin(); it != ModList.end(); ++it) {\n                        if(this->collapsUntilNextSeparator){\n                            if((*it)->modType == Lamp::Games::getInstance().currentGame->SeparatorModType()){\n                                this->collapsUntilNextSeparator = false;\n                            } else{\n                                i++; // this probable messes stuff up\n                                continue;\n                            }\n                        }\n\n                        ImGui::TableNextColumn();\n                        if(lampConfig::getInstance().listHighlight == i) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n\n\n                        std::string enabledButtonText = lampLang::LS(\"LAMPRAY_MODLIST_ENABLE\")+\"##\" + std::to_string(i);\n                        std::string disabledButtonText = lampLang::LS(\"LAMPRAY_MODLIST_DISABLE\")+\"##\" + std::to_string(i);\n                        if((*it)->modType == Lamp::Games::getInstance().currentGame->SeparatorModType()){\n                            enabledButtonText = (std::string)lampLang::LS(\"LAMPRAY_MODLIST_EXPND\")+\"##\" + std::to_string(i);\n                            disabledButtonText =  (std::string)lampLang::LS(\"LAMPRAY_MODLIST_COLL\")+\"##\" + std::to_string(i);\n                        }\n                        if((*it)->enabled) {\n                            if((*it)->modType == Lamp::Games::getInstance().currentGame->SeparatorModType()){\n                                this->collapsUntilNextSeparator = true;\n                            }\n\n                            ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)lampControl::getInstance().Colour_ButtonAlt);\n                            if (ImGui::Button(enabledButtonText.c_str())) {\n                                (*it)->enabled = false;\n                                Core::FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList, Games::getInstance().currentProfile);\n                            }\n                            ImGui::PopStyleColor(1);\n                        }else{\n                            if (ImGui::Button(disabledButtonText.c_str())) {\n                                (*it)->enabled = true;\n                                Core::FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList, Games::getInstance().currentProfile);\n                            }\n                        }\n                        if (ImGui::IsItemHovered(ImGuiHoveredFlags_None)) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n                        ImGui::TableNextColumn();\n                        if(lampConfig::getInstance().listHighlight == i) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,lampControl::getInstance().Colour_SearchHighlight);\n                        }\n\n                        std::filesystem::path path((*it)->ArchivePath);\n                        std::string cutname = path.filename().c_str();\n                        if(Lamp::Games::getInstance().currentGame->Ident().ShortHand == \"BG3\") {\n                            size_t pos = cutname.find('-');\n                            if (pos != std::string::npos) {\n                                cutname.erase(pos);\n                            }\n                        }\n                        // use full path string for separators\n                        if((*it)->modType == Lamp::Games::getInstance().currentGame->SeparatorModType()){\n                            cutname = path.c_str();\n                        }\n\n                        ImGui::Text(\"%s\", cutname.c_str());\n                        if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {\n                            ImGui::SetTooltip(\"%s\", (*it)->ArchivePath.c_str());\n                        }\n                        if (ImGui::IsItemHovered(ImGuiHoveredFlags_None)) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n\n                        auto contextId = \"MOD_NAME_CONTEXT_\" + std::to_string(i);\n                        auto renamePopupId = \"RENAME_MOD_\" + std::to_string(i);\n                        bool openRenamePopup = false;\n                        if (ImGui::BeginPopupContextItem(contextId.c_str())){\n\n\n\n\n                            if(ImGui::Selectable(lampLang::LS(\"LAMPRAY_MODLIST_MV_TOP\"))){\n                                moveModTo(it, 0);\n                                Core::FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList, Games::getInstance().currentProfile);\n                            }\n                            if(ImGui::Selectable(lampLang::LS(\"LAMPRAY_MODLIST_MV_BTTM\"))){\n                                moveModTo(it, std::distance(ModList.begin(), ModList.end()));\n                                Core::FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList, Games::getInstance().currentProfile);\n                            }\n\n                            if(ImGui::Selectable(lampLang::LS(\"LAMPRAY_MODLIST_CREATE_SEP\"))){\n                                Lamp::Games::getInstance().currentGame->registerArchive(modSeparatorDefaultText, Lamp::Games::getInstance().currentGame->SeparatorModType());\n                                // move the separator (now at the end of the ModList) to the index the user interacted at\n                                auto tmpSeparator = ModList.end() - 1;\n                                moveModTo(tmpSeparator, i);\n                                Core::FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList,Games::getInstance().currentProfile);\n                            }\n\n                            // restsrict to only mod separators for now as we do not store a separate \"name\", just a file path for mods\n                            if((*it)->modType == Lamp::Games::getInstance().currentGame->SeparatorModType()){\n                                // using a button as a Selectable did not work for some reason\n                                if(ImGui::Selectable(lampLang::LS(\"LAMPRAY_MODLIST_RENAME\"))){\n                                    // workaround for selectable not triggering the rename popup\n                                    openRenamePopup = true;\n                                    //ImGui::OpenPopup(renamePopupId.c_str());\n                                }\n                            }\n\n                            ImGui::EndPopup();\n                        }\n\n                        if(openRenamePopup == true){\n                            ImGui::OpenPopup(renamePopupId.c_str());\n                        }\n\n                        if(ImGui::BeginPopupModal(renamePopupId.c_str(), NULL, ImGuiWindowFlags_AlwaysAutoResize)){\n                            // 200 characters should hopefully be more than enough\n                            static char buf[200];\n                            ImGui::InputTextWithHint(\"##\", cutname.c_str(), buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_None);\n                            ImGui::Separator();\n\n                            if (ImGui::Button(lampLang::LS(\"LAMPRAY_CUSTOM_SV\"))) {\n                                (*it)->ArchivePath = buf;\n                                Core::FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList,Games::getInstance().currentProfile);\n\n                                ImGui::CloseCurrentPopup();\n                            }\n                            ImGui::SetItemDefaultFocus();\n                            ImGui::SameLine();\n                            // right-align the cancel button to help avoid potential misclicks\n                            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(\"Cancel\").x - ImGui::GetStyle().ItemSpacing.x);\n                            if (ImGui::Button(lampLang::LS(\"LAMPRAY_GOBACK\"))) {\n                                // Do nothing\n                                ImGui::CloseCurrentPopup();\n                            }\n                            ImGui::EndPopup();\n                        } // end rename popup modal\n\n\n                        // start drag and drop handling\n                        ImGuiDragDropFlags src_flags = 0;\n                        src_flags |= ImGuiDragDropFlags_SourceNoDisableHover;     // Keep the source displayed as hovered\n                        src_flags |= ImGuiDragDropFlags_SourceNoHoldToOpenOthers; // Because our dragging is local, we disable the feature of opening foreign treenodes/tabs while dragging\n                        src_flags |= ImGuiDragDropFlags_SourceAllowNullID; // apparently needed?\n                        //src_flags |= ImGuiDragDropFlags_SourceNoPreviewTooltip; // Hide the tooltip\n                        if (ImGui::BeginDragDropSource(src_flags))\n                        {\n                            if (!(src_flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))\n                                ImGui::Text(\"Moving \\\"%s\\\"\", cutname.c_str());\n                            ImGui::SetDragDropPayload(\"MODLIST_DND\", &i, sizeof(int));\n                            ImGui::EndDragDropSource();\n                        }\n\n                        if (ImGui::BeginDragDropTarget())\n                        {\n                            ImGuiDragDropFlags target_flags = 0;\n                            //target_flags |= ImGuiDragDropFlags_AcceptNoDrawDefaultRect; // Don't display the yellow rectangle\n                            target_flags |= ImGuiDragDropFlags_SourceAllowNullID;\n                            if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(\"MODLIST_DND\", target_flags))\n                            {\n                                auto movingMod = ModList.begin() + *(const int*)payload->Data; // get original position from the payload data\n                                moveModTo(movingMod, i);\n                                // save the change to the profile's Mod_List\n                                Core::FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList, Games::getInstance().currentProfile);\n                            }\n                            ImGui::EndDragDropTarget();\n                        }\n                        // end drag and drop handling\n\n                        ImGui::TableNextColumn();\n                        if(lampConfig::getInstance().listHighlight == i) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n\n\n                        if((*it)->modType != Lamp::Games::getInstance().currentGame->SeparatorModType()) {\n                            if (ImGui::BeginMenu(\n                                    (Lamp::Games::getInstance().currentGame->getModTypesMap()[(*it)->modType] + \"##\" +\n                                     std::to_string(i)).c_str())) {\n                                int y = 0;\n                                ImGui::MenuItem(cutname.c_str());\n                                ImGui::MenuItem(\"------------\");\n\n                                for (auto itt = Lamp::Games::getInstance().currentGame->getModTypes().begin();\n                                     itt != Lamp::Games::getInstance().currentGame->getModTypes().end(); ++itt) {\n                                    if((*itt).first != Lamp::Games::getInstance().currentGame->SeparatorModType()) {\n                                        if (ImGui::MenuItem(((*itt).second).c_str())) {\n                                            (*it)->modType = (*itt).first;\n                                            Core::FS::lampIO::saveModList(\n                                                    Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList);\n                                        }\n                                    }\n\n                                }\n\n                                ImGui::EndMenu();\n                            }\n                        }\n\n                        ImGui::TableNextColumn();\n                        if(lampConfig::getInstance().listHighlight == i) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n\n\n                        if(ImGui::Button((lampLang::LS(\"LAMPRAY_MODLIST_UP\")+\"##\"+std::to_string(i)))){\n                            moveUp(it);\n                            Core::FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList, Games::getInstance().currentProfile);\n                        }\n                        if (ImGui::IsItemHovered(ImGuiHoveredFlags_None)) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n                        ImGui::SameLine();\n                        ImGui::Text(\"%s\", (std::to_string(i)).c_str());\n                        if (ImGui::IsItemHovered(ImGuiHoveredFlags_None)) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n                        ImGui::SameLine();\n                        if(ImGui::Button((lampLang::LS(\"LAMPRAY_MODLIST_DOWN\")+\"##\"+std::to_string(i)))){\n                            moveDown(it);\n                            Core::FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList, Games::getInstance().currentProfile);\n                        }\n                        if (ImGui::IsItemHovered(ImGuiHoveredFlags_None)) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n\n                        ImGui::TableNextColumn();\n                        if(lampConfig::getInstance().listHighlight == i) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n\n                        if((*it)->modType != Lamp::Games::getInstance().currentGame->SeparatorModType()) {\n                            ImGui::Text(\"%s\", (*it)->timeOfUpdate.c_str());\n                        }\n\n                        if (ImGui::IsItemHovered(ImGuiHoveredFlags_None)) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n\n                        ImGui::TableNextColumn();\n                        if(lampConfig::getInstance().listHighlight == i) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n\n\n                        ImGui::BeginDisabled((*it)->enabled);\n\n\n                        if((*it)->modType == Lamp::Games::getInstance().currentGame->SeparatorModType()){\n                            if (ImGui::Button((\"  \"+lampLang::LS(\"LAMPRAY_MODLIST_REMOVE_SEP\")+\"  ##\" + std::to_string(i)))) {\n                                lampControl::getInstance().deletePos = i;\n                                ImGui::OpenPopup(lampLang::LS(\"LAMPRAY_MODLIST_CONFRIMDEL\"));\n\n                                ImGui::EndDisabled(); // fixes a crash when deleting items (when at least 1 mod has been enabled)\n                                break;\n                            }\n                        }else{\n                            if (ImGui::Button((lampLang::LS(\"LAMPRAY_MODLIST_REMOVE_MOD\")+\"##\" + std::to_string(i)))) {\n\n                                lampControl::getInstance().deletePos = i;\n                                ImGui::OpenPopup(lampLang::LS(\"LAMPRAY_MODLIST_CONFRIMDEL\"));\n\n                                ImGui::EndDisabled(); // fixes a crash when deleting items (when at least 1 mod has been enabled)\n                                break;\n                            }\n                        }\n\n\n\n\n\n\n                        if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && (*it)->enabled) {\n                            ImGui::SetTooltip(\"%s\", lampLang::LS(\"LAMPRAY_MODLIST_WARN\").c_str());\n                        }\n                        if (ImGui::IsItemHovered(ImGuiHoveredFlags_None)) {\n                            ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, lampControl::getInstance().Colour_SearchHighlight);\n                        }\n\n                        ImGui::EndDisabled();\n\n\n                        for (auto ittt = ExtraOptions.begin(); ittt != ExtraOptions.end(); ++ittt) {\n                            ImGui::TableNextColumn();\n                            if (ImGui::Button(ittt->first.c_str()))\n                                ittt->second = reinterpret_cast<bool *>(!ittt->second);\n\n                            if (ImGui::IsItemHovered(ImGuiHoveredFlags_None)) {\n                                ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, lampControl::getInstance().Colour_SearchHighlight);\n                            }\n                        }\n\n                        ImGui::TableNextRow();\n\n                        i++;\n                    }\n\n                    // Setup centering for the modal window\n                    ImVec2 center = ImGui::GetMainViewport()->GetCenter();\n                    ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));\n                    ImGui::SetNextWindowBgAlpha(0.9f); // This doesn't seem to mess anything up, but if it does search for new way to make modal bg more opaque.\n                    if(ImGui::BeginPopupModal(lampLang::LS(\"LAMPRAY_MODLIST_CONFRIMDEL\"), NULL, ImGuiWindowFlags_AlwaysAutoResize)){\n                        auto pendingDelete = ModList.begin() + lampControl::getInstance().deletePos;\n\n                        std::filesystem::path path((*pendingDelete)->ArchivePath);\n                        std::string delname = path.filename().c_str();\n                        // use full path string for separators\n                        if((*pendingDelete)->modType == Lamp::Games::getInstance().currentGame->SeparatorModType()){\n                            delname = path.c_str();\n                        }\n\n                        std::string promptMessage = lampLang::LS(\"LAMPRAY_MODLIST_WARN_SURE\");\n                        promptMessage.append(delname);\n                        promptMessage.append((std::string)lampLang::LS(\"LAMPRAY_MODLIST_WARN_FINAL\"));\n                        ImGui::Text(\"%s\", promptMessage.c_str());\n                        ImGui::Separator();\n\n                        if (ImGui::Button(lampLang::LS(\"LAMPRAY_MODLIST_DELETE\"), ImVec2(120, 0))) {\n                            if((*pendingDelete)->modType != Lamp::Games::getInstance().currentGame->SeparatorModType()){\n                                int deleteResult = std::remove(absolute(path).c_str());\n                                if(deleteResult != 0){\n                                    std::cout << \"Error deleting file: \" << absolute(path).c_str() << \"\\n   Error msg: \" << strerror(errno) << \"\\n\";\n                                }\n                            }\n\n                            std::cout << absolute(path).c_str() << std::endl;\n                            ModList.erase(pendingDelete);\n                            Core::FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList,Games::getInstance().currentProfile);\n\n                            lampControl::getInstance().deletePos = -1;\n                            ImGui::CloseCurrentPopup();\n                        }\n                        ImGui::SetItemDefaultFocus();\n                        ImGui::SameLine();\n                        // right-align the cancel button to help avoid potential misclicks on the delete button\n                        ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - 120);\n                        if (ImGui::Button(lampLang::LS(\"LAMPRAY_GOBACK\"), ImVec2(120, 0))) {\n                            // Do nothing\n                            lampControl::getInstance().deletePos = -1;\n                            ImGui::CloseCurrentPopup();\n                        }\n\n                        ImGui::EndPopup();\n                    } // end delete confirmation modal\n\n\n\n                    ImGui::EndTable();\n\n                    ImGuiPopupFlags outsideTablePopupFlags = 0;\n                    outsideTablePopupFlags |= ImGuiPopupFlags_NoOpenOverItems;\n                    outsideTablePopupFlags |= ImGuiPopupFlags_MouseButtonRight;\n                    outsideTablePopupFlags |= ImGuiPopupFlags_NoOpenOverExistingPopup;\n                    if (ImGui::BeginPopupContextWindow(\"OUTSIDE_TABLE_CONTEXT\", outsideTablePopupFlags)){\n                        if(ImGui::Selectable(lampLang::LS(\"LAMPRAY_MODLIST_CREATE_SEP\"))){\n                            Lamp::Games::getInstance().currentGame->registerArchive(modSeparatorDefaultText, Lamp::Games::getInstance().currentGame->SeparatorModType());\n                            Core::FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand, ModList,Games::getInstance().currentProfile);\n                        }\n                        ImGui::EndPopup();\n                    }\n                }\n            }\n\n        };\n\n        struct lampGameSettingsDisplayHelper{\n            explicit lampGameSettingsDisplayHelper(\n                    std::string displayString, std::string stringTarget, std::string toolTip, std::string keyPath);\n\n        public:\n\n            std::string  displayString;\n            std::string  stringTarget;\n            std::string  keyPath;\n            std::string  toolTip;\n\n\n            /**\n         * @brief Creates an ImGui menu for displaying game settings.\n         */\n            void createImGuiMenu(){\n                ImGui::Text(\"%s\", displayString.c_str());\n                ImGui::Text(\"%s\", toolTip.c_str());\n\n                // I think 4096 is the max length of a path on linux, but I may be wrong, and this may need to be changed.\n                static char gamePathBuf[4096] = \"\";\n                strncpy(gamePathBuf, stringTarget.c_str(), sizeof(gamePathBuf));\n\n                ImGui::InputText((\"##\"+keyPath+\"_text_input\").c_str(), gamePathBuf, 4096);\n\n                // if the user has changed the text field, save the changes to avoid them getting wiped out when they leave the text input\n                // I dislike updating the config file on each character change, but this should function for now\n                if(strcmp(gamePathBuf, stringTarget.c_str()) != 0){\n                    Lamp::Core::FS::lampIO::saveKeyData(keyPath,gamePathBuf,Lamp::Games::getInstance().currentGame->Ident().ShortHand);\n                    Lamp::Games::getInstance().currentGame->KeyInfo()[keyPath] = gamePathBuf;\n                }\n\n                ImGui::SameLine();\n\n                if(ImGui::Button(((std::string) lampLang::LS(\"LAMPRAY_SELECT_PATH\")+\"##\"+keyPath).c_str())) {\n                    nfdchar_t *outPath = NULL;\n                    nfdresult_t result = NFD_PickFolder(NULL, &outPath);\n\n                    if (result == NFD_OKAY) {\n                        puts(outPath);\n                        Lamp::Core::FS::lampIO::saveKeyData(keyPath,outPath,Lamp::Games::getInstance().currentGame->Ident().ShortHand);\n                        Lamp::Games::getInstance().currentGame->KeyInfo()[keyPath] = outPath;\n                    } else if (result == NFD_CANCEL) {\n                        puts(\"User pressed cancel.\");\n                    } else {\n                        printf(\"Error: %s\\n\", NFD_GetError());\n                    }\n                }\n            }\n\n        };\n\n        std::pair<int, int> deplopmentTracker; ///< A pair of integers representing deployment progress.\n        std::string deploymentStageTitle; ///< The title of the current deployment stage.\n        bool inDeployment = false; ///< Indicates whether Lampray is in the deployment process.\n\n\n    private:\n        /**\n         * @brief Private constructor for the `lampControl` class.\n         *\n         * The constructor is private to ensure that only one instance of `lampControl` can exist.\n         */\n        lampControl(){};\n\n\n    };\n\n\n}\n#endif //LAMP_LAMPCONTROL_H\n"
  },
  {
    "path": "Lampray/Control/lampGames.h",
    "content": "//\n// Created by charles on 27/09/23.\n//\n\n#ifndef LAMP_LAMPGAMES_H\n#define LAMP_LAMPGAMES_H\n\n#include \"../../game-data/BG3/BG3.h\"\n#include \"../../game-data/C77/C77.h\"\n\nnamespace Lamp {\n    class Games {\n    public:\n        /**\n         * @brief Represents a list of game controls.\n         */\n        std::vector<Game::gameControl *> gameList{\n                new Game::BG3,\n                new Game::C77\n        };\n\n        /**\n         * @brief Represents the name of the current user profile.\n         */\n        std::string currentProfile = \"Default\";\n\n        /**\n         * @brief Represents the index of the current game in the gameList.\n         */\n        int currentGameInt = 0;\n\n        /**\n         * @brief Represents the current game being played.\n         */\n        Game::gameControl *currentGame = gameList[currentGameInt];\n\n        /**\n         * @brief Provides access to the singleton instance of the Games class.\n         * @return The instance of the Games class.\n         */\n        static Games& getInstance()\n        {\n            static Games instance;\n            return instance;\n        }\n\n        /**\n         * @brief Deleted copy constructor to prevent copying of the Games instance.\n         */\n        Games(Games const&) = delete;\n\n        /**\n         * @brief Deleted assignment operator to prevent assignment of the Games instance.\n         */\n        void operator=(Games const&) = delete;\n\n    private:\n        /**\n         * @brief Private constructor to enforce singleton pattern and launch all gameControls.\n         */\n        Games(){\n            for (Game::gameControl * gameInstance: gameList) {\n                gameInstance->launch();\n            }\n        }\n    };\n};\n#endif //LAMP_LAMPGAMES_H\n"
  },
  {
    "path": "Lampray/Control/lampNotification.h",
    "content": "//\n// Created by SnazzyPanda on 2023-12-30.\n//\n\n#ifndef LAMP_LAMPNOTIFICATION_H\n#define LAMP_LAMPNOTIFICATION_H\n\n#include <iostream>\n#include <cmath>\n#include <algorithm>\n#include <string>\n#include <array>\n#include \"../../third-party/imgui/imgui.h\"\n\n\nnamespace Lamp::Core{\n    class lampNotification{\n    public:\n\n        static lampNotification& getInstance()\n        {\n            static lampNotification instance;\n            return instance;\n        }\n\n        struct NotificationColors{\n            ImColor notificationBG;\n            ImColor notificationBGHover;\n        };\n\n        NotificationColors DefaultTypeColors = {\n            ImColor(100, 100, 100, 255),\n            ImColor(50, 50, 50, 255),\n        };\n        NotificationColors InfoTypeColors = {\n            ImColor(0, 100, 200, 255),\n            ImColor(0, 100, 150, 255),\n        };\n        NotificationColors SuccessTypeColors = {\n            ImColor(0, 175, 50, 255),\n            ImColor(0, 125, 50, 255),\n        };\n        NotificationColors WarningTypeColors = {\n            ImColor(175, 100, 0, 255),\n            ImColor(150, 50, 0, 255),\n        };\n        NotificationColors ErrorTypeColors = {\n            ImColor(200, 0, 0, 255),\n            ImColor(150, 0, 0, 255),\n        };\n\n        // scale notifications about 25% larger than the rest of the content\n        float notificationScale = 1.25f;\n\n        void DisplayNotifications(){\n            ImGuiStyle& imStyle = ImGui::GetStyle();\n\n            int outerindex = 0;\n            for (auto it = this->notifications.begin(); it != this->notifications.end(); ++it) {\n                // get the coloring for this notification type\n                NotificationColors notifColors = this->getNotificationColors(outerindex);\n\n                if(this->notifications[outerindex].size() > 0){\n                    ImGui::PushStyleColor(ImGuiCol_ChildBg, notifColors.notificationBG.Value);\n                    ImGui::PushStyleColor(ImGuiCol_HeaderHovered, notifColors.notificationBGHover.Value);\n\n                    ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f, 0.0f)); // center-align text\n\n                    std::string NOTIF_BAR_CONTAINER_ID = \"NotificationBar_\" + outerindex;\n                    ImGui::BeginChild(NOTIF_BAR_CONTAINER_ID.c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0.0f), ImGuiChildFlags_AutoResizeY);\n\n                    ImGui::SetWindowFontScale(this->notificationScale);\n                    for (auto itt = (*it).begin(); itt != (*it).end(); ++itt) {\n                        std::string wrappedText = (*itt);\n                        auto spaceAvail = ImGui::GetContentRegionAvail().x;\n                        auto spaceNeeded = ImGui::CalcTextSize(wrappedText.c_str()).x;\n\n                        // attempt to manually wrap the text for long messages\n                        wrappedText = this->manuallyWrapText(wrappedText);\n\n                        if(ImGui::Selectable(wrappedText.c_str())){\n                            this->clearNotification(outerindex, itt);\n                            // go back 1 iteration as we just removed this item. Prevents a crash when clearning a notification.\n                            itt--;\n                        }\n                    }\n\n                    ImGui::EndChild();\n\n                    ImGui::PopStyleVar(1);\n                    ImGui::PopStyleColor(2);\n                }\n                outerindex++;\n            }\n        }\n\n        void pushInfoNotification(std::string message, bool oneTime = false){\n            this->addNotification(INFO, message, oneTime);\n        }\n        void pushSuccessNotification(std::string message, bool oneTime = false){\n            this->addNotification(SUCCESS, message, oneTime);\n        }\n        void pushWarningNotification(std::string message, bool oneTime = false){\n            this->addNotification(WARNING, message, oneTime);\n        }\n        void pushErrorNotification(std::string message, bool oneTime = false){\n            this->addNotification(ERROR, message, oneTime);\n        }\n        void pushNotification(std::string notiftype, std::string message, bool oneTime = false){\n            if(notiftype == \"info\"){\n                this->pushInfoNotification(message, oneTime);\n            } else if(notiftype == \"warning\"){\n                this->pushWarningNotification(message, oneTime);\n            } else if(notiftype == \"error\"){\n                this->pushErrorNotification(message, oneTime);\n            } else if(notiftype == \"success\"){\n                this->pushSuccessNotification(message, oneTime);\n            } else{\n                std::cout << \"Invalid notification type given: \" << notiftype << \"\\n\";\n            }\n        }\n\n    private:\n        // NOTE: Do not customize the values as it will break init/display logic\n        // (re-ordering everything except the NOTIFTYPE_END_PLACEHOLDER is fine)\n        enum NotificationTypes{\n            INFO,\n            SUCCESS,\n            WARNING,\n            ERROR,\n\n            NOTIFTYPE_END_PLACEHOLDER, // used for initializations. KEEP THIS AT THE END\n        };\n\n        std::array<NotificationColors, NOTIFTYPE_END_PLACEHOLDER> initNotificationColors(){\n            std::array<NotificationColors, NOTIFTYPE_END_PLACEHOLDER> colArray = {};\n            colArray[INFO] = InfoTypeColors;\n            colArray[SUCCESS] = SuccessTypeColors;\n            colArray[WARNING] = WarningTypeColors;\n            colArray[ERROR] = ErrorTypeColors;\n\n            return colArray;\n        }\n\n        // array containing a vector of notifications for each type, so typeList<notificationList>\n        std::array<std::vector<std::string>, NOTIFTYPE_END_PLACEHOLDER> notifications;\n        // oneTimeNotifications are notifications that should only be displayed once per session (we do not keep track between sessions)\n        std::vector<std::string> oneTimeNotifications;\n\n        void addNotification(int notiftype, std::string message, bool oneTime = false){\n            if((std::find(this->oneTimeNotifications.begin(), this->oneTimeNotifications.end(), message) != this->oneTimeNotifications.end())){\n                // we have already seen this oneTime notification, so do not try to display it again\n                return;\n            }\n\n            if(oneTime && (std::find(this->oneTimeNotifications.begin(), this->oneTimeNotifications.end(), message) == this->oneTimeNotifications.end())){\n                this->oneTimeNotifications.push_back(message);\n            }\n\n            // avoid adding duplicate notifications\n            if(std::find(this->notifications[notiftype].begin(), this->notifications[notiftype].end(), message) == this->notifications[notiftype].end()){\n                this->notifications[notiftype].push_back(message);\n            }\n        }\n        void clearNotification(int notiftype, std::vector<std::string>::iterator item){\n            this->notifications[notiftype].erase(item);\n        }\n\n\n        // first element is background, second is background hover (TODO: Better way to store these)\n        std::array<NotificationColors, NOTIFTYPE_END_PLACEHOLDER> notificationColorValues = initNotificationColors();\n\n        NotificationColors getNotificationColors(int notificationType){\n            // if not in that array, return default colors\n            if(notificationType < 0 || notificationType >= this->notificationColorValues.size()){\n                return DefaultTypeColors;\n            }\n\n            return this->notificationColorValues[notificationType];\n        }\n\n\n        std::string manuallyWrapText(std::string textToWrap){\n            auto spaceAvail = ImGui::GetContentRegionAvail().x;\n            auto spaceNeeded = ImGui::CalcTextSize(textToWrap.c_str()).x;\n            // no wrapping needed\n            if(spaceNeeded < spaceAvail){\n                return textToWrap;\n            }\n\n            std::vector<int> spacesToWrapAt = {};\n            int approxSizePerByte = std::ceil(spaceNeeded / textToWrap.size());\n            int charsThatFit = std::floor(spaceAvail / approxSizePerByte);\n            int prevspace = textToWrap.rfind(\" \", charsThatFit);\n\n            // calculate the spots to try to wrap at... NOTE: This does not work if there are not well palced spaces!\n            for(auto i = charsThatFit; i < textToWrap.size(); i+= charsThatFit){\n                prevspace = textToWrap.rfind(\" \", i);\n\n                // if we got a repeat prevspace, we are looping the same stuff\n                if(std::find(spacesToWrapAt.begin(), spacesToWrapAt.end(), prevspace) != spacesToWrapAt.end()){\n                    // we are looping, so just add a wrap anyway to continue...\n                    prevspace = i;\n                }\n\n                spacesToWrapAt.push_back(prevspace);\n                i = prevspace;\n            }\n\n            int offsetForInserts = 0;\n            // add newlines to manually wrap the text\n            for(auto it = spacesToWrapAt.begin(); it != spacesToWrapAt.end(); ++it){\n                int strpos = (*it) + offsetForInserts;\n                std::string spaceCheckString;\n                spaceCheckString += textToWrap.at(strpos);\n\n                if(spaceCheckString == \" \"){\n                    textToWrap.replace(strpos, 1, \"\\n\");\n                } else{\n                    // character was not a space, so insert instead of replace\n                    textToWrap.insert(strpos, \"\\n\");\n                    offsetForInserts += 1;\n                }\n            }\n\n            return textToWrap;\n        }\n    };\n}\n#endif //LAMP_LAMPNOTIFICATION_H\n"
  },
  {
    "path": "Lampray/Filesystem/lampExtract.cpp",
    "content": "//\n// Created by charles on 27/09/23.\n//\n\n#include <regex>\n#include \"lampFS.h\"\n#include \"bit7zlibrary.hpp\"\n#include \"../Control/lampGames.h\"\n#include \"bitarchivereader.hpp\"\n#include \"bitexception.hpp\"\n\nLamp::Core::FS::lampReturn Lamp::Core::FS::lampExtract::extract(const Base::lampMod::Mod *mod) {\n    if(!mod->enabled) return false;\n    std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath + Lamp::Games::getInstance().currentGame->Ident().ReadableName;\n    std::filesystem::create_directories(workingDir + \"/ext/\" + std::filesystem::path(mod->ArchivePath).filename().stem().string());\n\n    Base::lampLog::getInstance().log(\"Extracting File: \" + mod->ArchivePath, Base::lampLog::LOG);\n    if (std::regex_match((std::string)mod->ArchivePath, std::regex(\"^.*\\\\.(zip)$\"))) {\n        try {\n            bit7z::Bit7zLibrary lib{Lamp::Core::lampConfig::getInstance().bit7zLibraryLocation};\n            bit7z::BitArchiveReader reader{lib, mod->ArchivePath, bit7z::BitFormat::Zip};\n            reader.test();\n            reader.extract(workingDir + \"/ext/\" + std::filesystem::path(mod->ArchivePath).filename().stem().string());\n            return Base::lampLog::getInstance().pLog({1, \"Extraction Successful. : \"+ mod->ArchivePath},  Base::lampLog::LOG);\n        } catch (const bit7z::BitException &ex) {\n            return Base::lampLog::getInstance().pLog({0, \"Could not extract file : \"+ mod->ArchivePath},  Base::lampLog::ERROR,\n                                                     true, Base::lampLog::LMP_EXTRACTIONFALED);\n        }\n    } else if (std::regex_match((std::string)mod->ArchivePath, std::regex(\"^.*\\\\.(rar)$\"))) {\n        try {\n            bit7z::Bit7zLibrary lib{Lamp::Core::lampConfig::getInstance().bit7zLibraryLocation};\n            bit7z::BitArchiveReader reader{lib, mod->ArchivePath, bit7z::BitFormat::Rar5};\n            reader.test();\n            reader.extract(workingDir + \"/ext/\" + std::filesystem::path(mod->ArchivePath).filename().stem().string());\n            return Base::lampLog::getInstance().pLog({1, \"Extraction Successful. : \"+ mod->ArchivePath},  Base::lampLog::LOG);\n        } catch (const bit7z::BitException &ex) {\n            try {\n                bit7z::Bit7zLibrary lib{Lamp::Core::lampConfig::getInstance().bit7zLibraryLocation};\n                bit7z::BitArchiveReader reader{lib, mod->ArchivePath, bit7z::BitFormat::Rar};\n                reader.test();\n                reader.extract(workingDir + \"/ext/\" + std::filesystem::path(mod->ArchivePath).filename().stem().string());\n                return Base::lampLog::getInstance().pLog({1, \"Extraction Successful. : \"+ mod->ArchivePath},  Base::lampLog::LOG);\n            } catch (const bit7z::BitException &ex2) {\n                return Base::lampLog::getInstance().pLog({0, \"Could not extract file : \"+ mod->ArchivePath + \"\\nMessages:\\n\" + ex.what() + \"\\n\" + ex2.what()},\n                                                     Base::lampLog::ERROR, true, Base::lampLog::LMP_EXTRACTIONFALED);\n            }\n        }\n    } else if (std::regex_match((std::string)mod->ArchivePath, std::regex(\"^.*\\\\.(7z)$\"))) {\n        try {\n            bit7z::Bit7zLibrary lib{Lamp::Core::lampConfig::getInstance().bit7zLibraryLocation};\n            bit7z::BitArchiveReader reader{lib, mod->ArchivePath, bit7z::BitFormat::SevenZip};\n            reader.test();\n            reader.extract(workingDir + \"/ext/\" + std::filesystem::path(mod->ArchivePath).filename().stem().string());\n            return Base::lampLog::getInstance().pLog({1, \"Extraction Successful. : \"+ mod->ArchivePath},  Base::lampLog::LOG);\n        } catch (const bit7z::BitException &ex) {\n            return Base::lampLog::getInstance().pLog({0, \"Could not extract file : \"+ mod->ArchivePath},  Base::lampLog::ERROR,\n                                                     true, Base::lampLog::LMP_EXTRACTIONFALED);\n        }\n    }\n    else{\n        return false;\n    }\n    return false;\n}\n\nLamp::Core::lampReturn Lamp::Core::FS::lampExtract::moveModSpecificFileType(const Lamp::Core::Base::lampMod::Mod *mod,\n                                                                            Lamp::Core::FS::lampString extension,\n                                                                            Lamp::Core::lampString localExtractionPath) {\n    std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath +\n                             Lamp::Games::getInstance().currentGame->Ident().ReadableName;\n    for (const auto& entry : fs::recursive_directory_iterator(workingDir + \"/ext/\" + std::filesystem::path(mod->ArchivePath).filename().stem().string())) {\n        copyFilesWithExtension(\n                workingDir + \"/ext/\" + std::filesystem::path(mod->ArchivePath).filename().stem().string(),\n                workingDir + \"/\" + localExtractionPath, extension);\n    }\n\n    return {1, \"\"};\n}\n\nLamp::Core::lampReturn Lamp::Core::FS::lampExtract::moveModSpecificFolder(const Lamp::Core::Base::lampMod::Mod *mod,\n                                                                          Lamp::Core::FS::lampString extension,\n                                                                          Lamp::Core::lampString localExtractionPath) {\n    std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath + Lamp::Games::getInstance().currentGame->Ident().ReadableName;\n    return caseInsensitiveFolderCopyRecursive(workingDir + \"/ext/\" + std::filesystem::path(mod->ArchivePath).filename().stem().string(), workingDir+\"/\"+localExtractionPath, extension);\n}\n\n\n\nLamp::Core::lampReturn\nLamp::Core::FS::lampExtract::copyFile(const std::filesystem::path &source, const std::filesystem::path &destination) {\n    std::ifstream sourceFile(source, std::ios::binary);\n    std::ofstream destinationFile(destination, std::ios::binary);\n\n    if (!sourceFile.is_open() || !destinationFile.is_open()) {\n        return {false, \"Failed to open source or destination file.\"};\n    }\n\n    destinationFile << sourceFile.rdbuf();\n    return {true, \"File copied successfully.\"};\n}\n\nLamp::Core::lampReturn Lamp::Core::FS::lampExtract::copyDirectoryRecursively(const std::filesystem::path &source,\n                                                           const std::filesystem::path &destination) {\n    for (const auto& entry : fs::directory_iterator(source)) {\n        fs::path destinationEntry = destination / entry.path().filename();\n\n        if (fs::is_directory(entry)) {\n            fs::create_directories(destinationEntry);\n            lampReturn result = copyDirectoryRecursively(entry, destinationEntry);\n            if (!result) {\n                return result; // Propagate the error if copying failed\n            }\n        } else if (fs::is_regular_file(entry)) {\n            lampReturn result = copyFile(entry.path(), destinationEntry);\n            if (!result) {\n                return result; // Propagate the error if copying failed\n            }\n            std::cout << \"File copied to: \" << destinationEntry << std::endl;\n        }\n    }\n\n    return {true, \"Directory copied successfully.\"};\n}\n\nLamp::Core::lampReturn Lamp::Core::FS::lampExtract::caseInsensitiveFolderCopyRecursive(const std::filesystem::path &sourceDirectory,\n                                                                     const std::filesystem::path &destinationDirectory,\n                                                                     const std::string &targetDirectoryName) {\n    for (const auto& entry : fs::recursive_directory_iterator(sourceDirectory)) {\n        if (fs::is_directory(entry) &&\n            caseInsensitiveStringCompare(entry.path().filename().string(), targetDirectoryName)) {\n            lampReturn result = copyDirectoryRecursively(entry, destinationDirectory);\n            if (!result) {\n                return result; // Propagate the error if copying failed\n            }\n        }\n    }\n\n    return {true, \"Folder copied successfully.\"};\n}\n\n\nLamp::Core::lampReturn Lamp::Core::FS::lampExtract::copyFilesWithExtension(const std::filesystem::path &sourceDirectory,\n                                                         const std::filesystem::path &destinationDirectory,\n                                                         const std::string &extension) {\n    for (const auto& entry : fs::recursive_directory_iterator(sourceDirectory)) {\n        if (fs::is_regular_file(entry)) {\n            std::string fileExtension = entry.path().extension().string();\n            if (!fileExtension.empty() && caseInsensitiveStringCompare(fileExtension.substr(1), extension)) {\n                fs::path destinationFile = destinationDirectory / entry.path().filename();\n                lampReturn result = copyFile(entry.path(), destinationFile);\n                if (!result) {\n                    return result; // Propagate the error if copying failed\n                }\n                std::cout << \"File copied to: \" << destinationFile << std::endl;\n            }\n        }\n    }\n\n    return {true, \"Files copied successfully.\"};\n}\n\n\nbool Lamp::Core::FS::lampExtract::caseInsensitiveStringCompare(const std::string &str1, const std::string &str2) {\n    return std::equal(str1.begin(), str1.end(), str2.begin(), str2.end(),\n                      [](char a, char b) {\n                          return tolower(a) == tolower(b);\n                      });\n}\n\n\n\n"
  },
  {
    "path": "Lampray/Filesystem/lampFS.h",
    "content": "//\n// Created by charles on 27/09/23.\n//\n\n#ifndef LAMP_LAMPFS_H\n#define LAMP_LAMPFS_H\n\n#include <string>\n#include <filesystem>\n#include \"../Base/lampBase.h\"\n#include \"../Control/lampConfig.h\"\n\nnamespace Lamp::Core::FS{\n    typedef Base::lampTypes::lampString lampString;\n    typedef Base::lampTypes::lampReturn lampReturn;\n    /**\n     * @brief The lampExtract class provides utility functions for extracting and moving mod files.\n     */\n    class lampExtract {\n    public:\n        static lampReturn extract(const Base::lampMod::Mod * mod);\n        static lampReturn moveModSpecificFileType(const Base::lampMod::Mod * mod, Lamp::Core::FS::lampString extension, lampString localExtractionPath);\n        static lampReturn moveModSpecificFolder(const Base::lampMod::Mod * mod, Lamp::Core::FS::lampString extension, lampString localExtractionPath);\n\n    private:\n        /**\n         * @brief Copies a file from the source path to the destination path.\n         *\n         * @param source The source file path.\n         * @param destination The destination file path.\n         * @return A lampReturn object indicating the copy operation result.\n         */\n        static lampReturn copyFile(const std::filesystem::path& source, const std::filesystem::path& destination);\n\n        /**\n         * @brief Recursively copies a directory and its contents from the source path to the destination path.\n         *\n         * @param source The source directory path.\n         * @param destination The destination directory path.\n         * @return A lampReturn object indicating the copy operation result.\n         */\n        static lampReturn copyDirectoryRecursively(const std::filesystem::path& source, const std::filesystem::path& destination);\n\n        /**\n         * @brief Recursively copies a folder with a case-insensitive name match from source to destination.\n         *\n         * @param sourceDirectory The source directory path.\n         * @param destinationDirectory The destination directory path.\n         * @param targetDirectoryName The target folder name to match case-insensitively.\n         * @return A lampReturn object indicating the copy operation result.\n         */\n        static lampReturn caseInsensitiveFolderCopyRecursive(const std::filesystem::path& sourceDirectory, const std::filesystem::path& destinationDirectory, const std::string& targetDirectoryName);\n\n        /**\n         * @brief Copies files with a specific file extension from the source directory to the destination directory.\n         *\n         * @param sourceDirectory The source directory path.\n         * @param destinationDirectory The destination directory path.\n         * @param extension The file extension to filter files by.\n         * @return A lampReturn object indicating the copy operation result.\n         */\n        static lampReturn copyFilesWithExtension(const std::filesystem::path& sourceDirectory, const std::filesystem::path& destinationDirectory, const std::string& extension);\n\n        /**\n         * @brief Performs a case-insensitive string comparison between two strings.\n         *\n         * @param str1 The first string to compare.\n         * @param str2 The second string to compare.\n         * @return True if the strings are equal (case-insensitive), false otherwise.\n         */\n        static bool caseInsensitiveStringCompare(const std::string& str1, const std::string& str2);\n    };\n\n    /**\n     * @brief The lampIO class provides input/output utility functions for loading and saving mod-related data.\n     */\n    class lampIO {\n    public:\n        /**\n         * @brief Loads the list of mods for a specified game and profile.\n         *\n         * This function loads the list of mods for a specific game and profile from an XML file.\n         *\n         * @param game The name of the game.\n         * @param profileName The name of the profile (default is \"Default\").\n         * @return A vector of lampMod::Mod pointers representing the loaded mods.\n         */\n        static std::vector<Base::lampMod::Mod*> loadModList(Lamp::Core::FS::lampString game, lampString profileName = \"Default\");\n\n        /**\n         * @brief Saves the list of mods to an XML file.\n         *\n         * This function saves the list of mods to an XML file for a specified game and profile.\n         *\n         * @param game The name of the game.\n         * @param ModList The vector of lampMod::Mod pointers to be saved.\n         * @param profileName The name of the profile (default is \"Default\").\n         * @return A lampReturn object indicating the save operation result.\n         */\n        static lampReturn saveModList(Lamp::Core::FS::lampString game, std::vector<Base::lampMod::Mod*> ModList, lampString profileName = \"Default\");\n\n        /**\n         * @brief Saves key-value data to a configuration file.\n         *\n         * This function saves key-value data to a configuration file for a specified game.\n         *\n         * @param key The key to save.\n         * @param data The data associated with the key.\n         * @param game The name of the game.\n         * @return A lampReturn object indicating the save operation result.\n         */\n        static lampReturn saveKeyData(lampString key, lampString data, Lamp::Core::FS::lampString game);\n\n        /**\n         * @brief Loads key-value data from a configuration file.\n         *\n         * This function loads key-value data from a configuration file for a specified game.\n         *\n         * @param key The key to load.\n         * @param game The name of the game.\n         * @return A lampReturn object containing the loaded data.\n         */\n        static lampReturn loadKeyData(lampString key, Lamp::Core::FS::lampString game);\n\n        /**\n         * @brief Empties a folder with optional filtering by file extension.\n         *\n         * This function empties a folder by deleting its contents, optionally filtering by a specific file extension.\n         *\n         * @param Path The path of the folder to empty.\n         * @param SpecificExtension The file extension to filter files by (default is empty, which means all files will be deleted).\n         * @return A lampReturn object indicating the operation result.\n         */\n        static lampReturn emptyFolder(lampString Path, lampString SpecificExtension = \"\");\n\n         static void fileDrop(const char *inputPath);\n\n        /**\n         * @brief Downloads a file from a URL and saves it with a specified filename.\n         *\n         * This function downloads a file from a given URL and saves it with the specified output filename.\n         *\n         * @param url The URL of the file to download.\n         * @param output_filename The filename to save the downloaded file as.\n         * @return A lampReturn object indicating the download operation result.\n         */\n        static lampReturn downloadFile(lampString url, lampString output_filename);\n    };\n\n\n/**\n * @class lampShare\n * @brief A class for handling profile export and import operations.\n */\n    class lampShare {\n    public:\n        /**\n         * @brief Export a user profile to a compressed file.\n         *\n         * This function exports a user profile to a compressed .lampProfile file.\n         *\n         * @param profileName The name of the profile to be exported.\n         */\n        static void exportProfile(std::string profileName);\n\n        /**\n         * @brief Import a user profile from a compressed file.\n         *\n         * This function allows users to import a previously exported profile.\n         */\n        static void importProfile();\n\n    private:\n        /**\n         * @brief Insert XML data into an existing file.\n         *\n         * This private function takes a filename and an XML node and inserts the XML data at the start of the file.\n         *\n         * @param filename The name of the file to insert XML data into.\n         * @param metadata The XML node containing the metadata to be inserted.\n         */\n        static void InsertXMLintoFile(const std::string& filename, const pugi::xml_node& metadata);\n\n        /**\n         * @brief Extract XML data from a file and return it as a string.\n         *\n         * This private function reads a file line by line and extracts the XML content between \"<metadata>\" and \"</metadata>\" tags.\n         *\n         * @param filename The name of the file from which to extract XML data.\n         * @param content[out] A string containing the extracted XML data.\n         */\n        static void ExtractXMLfromFile(const std::string& filename, std::string& content);\n\n        /**\n         * @brief Compress a file using LZ4HC.\n         *\n         * This private function compresses an input file using the LZ4HC compression algorithm and saves the result in an output file.\n         *\n         * @param inputFile The path to the input file to be compressed.\n         * @param outputFile The path to the output file where the compressed data will be saved.\n         * @return True if compression is successful, false otherwise.\n         */\n        static bool compressFile(const std::string& inputFile, const std::string& outputFile);\n\n        /**\n         * @brief Decompress a file using LZ4HC.\n         *\n         * This private function decompresses an input file that was compressed with LZ4HC and saves the result in an output file.\n         *\n         * @param inputFile The path to the input compressed file.\n         * @param outputFile The path to the output file where the decompressed data will be saved.\n         * @param volumeSize The expected volume size of the decompressed data.\n         * @return True if decompression is successful, false otherwise.\n         */\n        static bool decompressFile(const std::string& inputFile, const std::string& outputFile, std::uintmax_t volumeSize);\n    };\n\n    /**\n     * @class lampUpdate\n     * @brief A singleton class for checking and retrieving updates.\n     *\n     * The `lampUpdate` class provides functionality to check for updates and retrieve expression data.\n     */\n    class lampUpdate {\n    public:\n        /**\n         * @brief Get the instance of the `lampUpdate` class.\n         * @return A reference to the `lampUpdate` instance.\n         *\n         * This static function allows access to the singleton instance of the `lampUpdate` class.\n         */\n        static lampUpdate& getInstance()\n        {\n            static lampUpdate instance;\n            return instance;\n        }\n\n        /**\n         * @brief Deleted copy constructor to prevent copying of the singleton instance.\n         */\n        lampUpdate(lampUpdate const&) = delete;\n\n        /**\n         * @brief Deleted assignment operator to prevent copying of the singleton instance.\n         */\n        void operator=(lampUpdate const&)  = delete;\n\n        /**\n         * @brief Callback function for writing received data.\n         *\n         * This static function is used as a callback to write received data into a string.\n         *\n         * @param contents A pointer to the received data.\n         * @param size The size of each data element.\n         * @param nmemb The number of data elements.\n         * @param output A pointer to the output string where data will be appended.\n         * @return The total size of data written.\n         */\n        static size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {\n            size_t total_size = size * nmemb;\n            output->append(static_cast<char*>(contents), total_size);\n            return total_size;\n        }\n\n        /**\n         * @brief The version number of the software.\n         */\n        std::string versionNumber = \"1.3.2\"; // x-release-please-version\n\n        /**\n         * @brief Check for updates.\n         *\n         * This function checks for updates to the software.\n         */\n        void checkForUpdates();\n\n        void getExpression();\n\n    private:\n        /**\n         * @brief Private constructor to enforce singleton pattern.\n         */\n        lampUpdate(){}\n    };\n\n    class lampTrack{\n    private:\n\n        enum trackedFileType{\n            GameFile = 0,\n            ModFile\n        };\n\n        struct trackedFile{\n        public:\n            std::string game;\n            std::string gameLocation; // relative path from games root.\n            std::string fileHash;\n            trackedFileType type; // 0 - GameFile, 1 - ModFile\n        };\n\n\n\n\n        constexpr static unsigned int crc32(const char* data, size_t length) {\n            unsigned int crc = 0xFFFFFFFF;\n            for (size_t i = 0; i < length; ++i) {\n                crc ^= static_cast<unsigned int>(data[i]);\n                for (int j = 0; j < 8; ++j) {\n                    crc = (crc >> 1) ^ (0xEDB88320 & (-int(crc & 1)));\n                }\n            }\n            return ~crc;\n        }\n\n        static bool doesHashExist(const std::string& game, const std::string& hash);\n\n        static bool doesFilenameExist(const std::string& game, const std::string& filename);\n\n        static std::string getHash(std::filesystem::path FilePath);\n\n        static void loadTracker(const std::string& filename);\n        static void saveTracker(const std::string& filename, const std::vector<trackedFile>& files);\n\n        static void recursiveCopyWithIgnore(const std::filesystem::path &source, const std::filesystem::path &destination, const std::vector<std::string> &ignoreFolders,std::filesystem::copy_options options, std::string game);\n        static void copyExtensionWithFileTypeIgnore(const std::filesystem::path &sourceDir, const std::filesystem::path &destDir, std::string nonIgnoredExtension,std::filesystem::copy_options options, std::string game);\n        static void copyAndOperate(const std::filesystem::path& source, const std::filesystem::path& destination, std::string game);\n        static lampReturn trackOperation(const std::string& file, const std::string& destination, const std::string &game);\n\n        static std::vector<trackedFile>& getTrackedFiles() {\n            static std::vector<trackedFile> trackedFiles;\n            return trackedFiles;\n        }\n\n        static void replaceGameFile(const std::filesystem::path& gameFilePath, const std::filesystem::path& gameFilesFolder);\n        static void deleteModFile(const std::filesystem::path& modFilePath);\n\n        public:\n\n\n\n\n        struct handleFileDescriptor{\n        public:\n            enum operation{\n                copyFile, // directly copy filePath to target.\n                copyFolder, // copy a folder and its content recursively.\n                copyFilesIgnoreExt, // copy a folder and its content recursively ignoring a folder with the name of extName.\n                copyOnlyExt, // copy a folder and its content recursively with the extension of extName.\n            };\n\n            enum mode{\n                direct,\n                skipExisting,\n                updateExisting\n            };\n\n            operation handlerOperation;\n            mode handlerMode;\n            std::filesystem::path filePath;\n            std::filesystem::path target;\n            std::filesystem::copy_options copyOperationOptions = std::filesystem::copy_options::recursive;\n            std::string gameFullName;\n            std::string extName;\n\n            handleFileDescriptor(operation handlerOperation, mode handlerMode, std::filesystem::path filePath,\n                                 std::filesystem::path target, std::string extName, std::string gameFullName)\n                    : handlerOperation(handlerOperation), filePath(filePath), extName(extName), target(target),\n                      handlerMode(handlerMode), gameFullName(gameFullName) {}\n        };\n        static Lamp::Core::lampReturn handleFile(handleFileDescriptor descriptor);\n        //static void forcefulTrack();\n\n        static void reset(std::string gameFullReadableName);\n\n        };\n}\n#endif //LAMP_LAMPFS_H\n"
  },
  {
    "path": "Lampray/Filesystem/lampIO.cpp",
    "content": "//\n// Created by charles on 27/09/23.\n//\n#include <map>\n#include <regex>\n#include \"lampFS.h\"\n#include \"../Control/lampControl.h\"\n#include \"bit7zlibrary.hpp\"\n#include \"bitfilecompressor.hpp\"\n#include \"bitarchivewriter.hpp\"\n\n\nstd::vector<Lamp::Core::Base::lampMod::Mod *> Lamp::Core::FS::lampIO::loadModList(Lamp::Core::FS::lampString game, Lamp::Core::FS::lampString profileNameS) {\n    std::string profileName = (std::string)profileNameS;\n    if((std::string)profileNameS == \"\"){\n        profileName = \"Default\";\n    }\n\n\n    std::string xmlPath = Core::lampConfig::getInstance().saveDataPath + game + \".mdf\";\n    std::vector<Base::lampMod::Mod *> newList;\n    pugi::xml_document doc;\n    if (doc.load_file(xmlPath.c_str())) {\n        pugi::xml_node profileNode = doc.child(\"root\").find_child_by_attribute(\"Profile\", \"Name\", profileName.c_str());\n        std::cout << profileNode.attribute(\"Name\").value() << \"\" << std::endl;\n        if ((std::string)Lamp::Core::FS::lampString(profileNode.attribute(\"Name\").value()) == (std::string)Lamp::Core::FS::lampString(profileName)){\n            for (pugi::xml_node modNode = profileNode.child(\"Mod\"); modNode; modNode = modNode.next_sibling(\n                    \"Mod\")) {\n                std::string archivePath = modNode.attribute(\"ArchivePath\").as_string();\n                int modType = modNode.attribute(\"modType\").as_int();\n                bool enabled = modNode.attribute(\"enabled\").as_bool();\n\n                Base::lampMod::Mod *temp = new Base::lampMod::Mod{archivePath, modType, enabled};\n\n                if (modNode.attribute(\"timeOfUpdate\")) {\n                    temp->timeOfUpdate = modNode.attribute(\"timeOfUpdate\").as_string();\n                    if (temp->timeOfUpdate == \"Unknown\") {\n                        temp->timeOfUpdate = lampControl::getInstance().getFormattedTimeAndDate();\n                    }\n\n                } else {\n                    temp->timeOfUpdate = lampControl::getInstance().getFormattedTimeAndDate();\n                }\n                newList.emplace_back(temp);\n            }\n        }\n\n        for (const Base::lampMod::Mod *mod: newList) {\n            Base::lampLog::getInstance().log(\n                    \"Loaded Mod: \" + mod->ArchivePath + \"| Mod Type: \" + std::to_string(mod->modType) +\n                    \"| Enabled :\" + std::to_string(mod->enabled));\n        }\n\n        return newList;\n    } else {\n        Base::lampLog::getInstance().log(\"Could not load modlist.\", Base::lampLog::ERROR, false,\n                                         Base::lampLog::LMP_MODLISTSAVEFAILED);\n    }\n\n\n    return std::vector<Base::lampMod::Mod *>();\n}\n\nLamp::Core::FS::lampReturn\nLamp::Core::FS::lampIO::saveModList(Lamp::Core::FS::lampString game, std::vector<Base::lampMod::Mod *> ModList, Lamp::Core::FS::lampString profileName) {\n\n    if (Lamp::Games::getInstance().currentProfile != \"Default\") {\n        profileName = Lamp::Games::getInstance().currentProfile;\n    }\n    std::string xmlPath = Core::lampConfig::getInstance().saveDataPath + game + \".mdf\";\n    pugi::xml_document doc;\n    doc.load_file(xmlPath.c_str());\n    pugi::xml_node rootNode = doc.child(\"root\");\n\n    if (!rootNode) {\n        rootNode = doc.append_child(\"root\");\n    }\n\n    pugi::xml_node profileNode = rootNode.find_child_by_attribute(\"Profile\", \"Name\", profileName);\n\n    if (!profileNode) {\n        // If the Profile node doesn't exist, create it.\n        pugi::xml_node profileNewNode = rootNode.append_child(\"Profile\");\n        profileNewNode.append_attribute(\"Name\").set_value(profileName);\n\n        for (const Base::lampMod::Mod *mod : ModList) {\n            mod->serialize(profileNewNode);\n        }\n    } else {\n        // Profile node with the same name exists, you can update it if needed.\n\n        profileNode.remove_children();\n\n        for (const Base::lampMod::Mod *mod : ModList) {\n            mod->serialize(profileNode);\n        }\n    }\n\n    if (doc.save_file(xmlPath.c_str())) {\n        return true;\n    } else {\n        std::cerr << \"Failed to save XML file.\" << std::endl;\n        return false;\n    }\n}\n\nLamp::Core::FS::lampReturn\nLamp::Core::FS::lampIO::saveKeyData(Lamp::Core::FS::lampString key, Lamp::Core::FS::lampString data, Lamp::Core::FS::lampString game) {\n    lampString actual = game;\n    if(game == \"\") {\n        actual = (std::string)Lamp::Games::getInstance().currentGame->Ident().ShortHand;;\n    }\n    Base::lampLog::getInstance().log(\"Saving \" + actual + \":\" + key + \":\" + data);\n    std::string xmlPath = Core::lampConfig::getInstance().ConfigDataPath + \"conf.mdf\";\n\n    pugi::xml_document doc;\n    if (doc.load_file(xmlPath.c_str())) {\n        pugi::xml_node rootNode = doc.child(\"root\");\n        pugi::xml_node gameNode = rootNode.find_child_by_attribute(\"game\", \"name\", actual.c_str());\n        if (!gameNode) {\n            gameNode = rootNode.append_child(\"game\");\n            gameNode.append_attribute(\"name\").set_value(actual.c_str());\n        }\n\n        pugi::xml_node keyNode = gameNode.child(key);\n        if (keyNode) {\n            gameNode.remove_child(key);\n        }\n\n        keyNode = gameNode.append_child(key);\n        keyNode.text().set(data);\n\n        if (doc.save_file(xmlPath.c_str())) {\n            return Lamp::Core::FS::lampReturn(1);\n        }\n    } else {\n        pugi::xml_node rootNode = doc.append_child(\"root\");\n        pugi::xml_node gameNode = rootNode.append_child(\"game\");\n        gameNode.append_attribute(\"name\").set_value(actual.c_str());\n\n        pugi::xml_node keyNode = gameNode.append_child(key);\n        keyNode.text().set(data);\n\n        if (doc.save_file(xmlPath.c_str())) {\n            return Lamp::Core::FS::lampReturn(1);\n        }\n    }\n    Base::lampLog::getInstance().log(\"Failed to save \" + game + \":\" + key + \":\" + \":\" + data,\n                                     Base::lampLog::ERROR, true, Base::lampLog::LMP_KEYSAVEFAILED);\n    return Lamp::Core::FS::lampReturn(0);\n}\n\n\n\nLamp::Core::FS::lampReturn Lamp::Core::FS::lampIO::loadKeyData(Lamp::Core::FS::lampString key, Lamp::Core::FS::lampString game) {\n    std::string actual = game;\n    if(game == \"\") {\n        actual = (std::string)Lamp::Games::getInstance().currentGame->Ident().ShortHand;;\n    }\n    Base::lampLog::lampLog::getInstance().log(\"Loading \" + actual + \":\" + key);\n    std::string xmlPath = Core::lampConfig::getInstance().ConfigDataPath + \"conf.mdf\";\n\n    pugi::xml_document doc;\n\n    if (doc.load_file(xmlPath.c_str())) {\n        pugi::xml_node rootNode = doc.child(\"root\");\n        pugi::xml_node gameNode = rootNode.find_child_by_attribute(\"game\", \"name\", actual.c_str());\n\n        if (gameNode) {\n            pugi::xml_node keyNode = gameNode.child(key);\n\n            if (keyNode) {\n                return Lamp::Core::FS::lampReturn(1, keyNode.text().as_string());\n            }\n        }\n    }\n    Base::lampLog::getInstance().log(\"Unable to load \" + actual + \":\" + key, Base::lampLog::WARNING, false,\n                                     Base::lampLog::LMP_KEYLOADFAILED);\n    return Lamp::Core::FS::lampReturn(0, \"\");\n\n}\n\n\nvoid Lamp::Core::FS::lampIO::fileDrop(const char *inputPath) {\n        // Thank you! Roi Danton on stackoverflow for this clean code.\n        std::string pth = inputPath;\n        Base::lampLog::getInstance().log(\"File Drop Detected: \" + pth);\n        std::filesystem::path path(inputPath);\n        std::error_code ec;\n        if (std::filesystem::is_regular_file(path, ec)) {\n            if (std::regex_match(path.filename().string(), std::regex(\"^.*\\\\.(zip|rar|7z)$\"))) {\n                std::filesystem::path targetDIR = Core::lampConfig::getInstance().archiveDataPath +\n                        Lamp::Games::getInstance().currentGame->Ident().ReadableName; // Roi Danton many thanks again!\n                auto target = targetDIR / path.filename();\n                try {\n                    std::filesystem::create_directories(targetDIR);\n                    if(!std::filesystem::exists(target)){\n                        std::filesystem::copy_file(path, target, std::filesystem::copy_options::overwrite_existing);\n                    }\n                    Lamp::Games::getInstance().currentGame->registerArchive(target.string());\n\n                }\n                catch (std::exception &e) {\n                    std::string ex = e.what();\n                    Base::lampLog::getInstance().log(\"File Drop Failed: \" + ex, Base::lampLog::ERROR, true,\n                                                     Base::lampLog::LMP_NOFILEDROP);\n                }\n            } if(std::regex_match(path.filename().string(), std::regex(\"^.*\\\\.(pak)$\"))){\n                if(Lamp::Games::getInstance().currentGame->Ident().ReadableName == \"Baldur's Gate 3\") {\n                    bit7z::Bit7zLibrary lib{ Lamp::Core::lampConfig::getInstance().bit7zLibraryLocation };\n                    bit7z::BitArchiveWriter archive{ lib, bit7z::BitFormat::SevenZip };\n                    archive.addFile(path);\n                    archive.compressTo(lampConfig::getInstance().archiveDataPath+\"/\"+path.filename().string()+\" LMP.zip\" );\n\n\n\n\n                    std::filesystem::path targetDIR = Core::lampConfig::getInstance().archiveDataPath +\n                                                      Lamp::Games::getInstance().currentGame->Ident().ReadableName; // Roi Danton many thanks again!\n                    auto target = targetDIR / path.filename();\n                    try {\n                        std::filesystem::create_directories(targetDIR);\n                        if(!std::filesystem::exists(target)){\n                            std::filesystem::copy_file(lampConfig::getInstance().archiveDataPath+\"/\"+path.filename().string()+\" LMP.zip\", target, std::filesystem::copy_options::overwrite_existing);\n                        }\n\n                        Lamp::Games::getInstance().currentGame->registerArchive(target.string());\n\n                    }\n                    catch (std::exception &e) {\n                        std::string ex = e.what();\n                        Base::lampLog::getInstance().log(\"File Drop Failed: \" + ex, Base::lampLog::ERROR, true,\n                                                         Base::lampLog::LMP_NOFILEDROP);\n                    }\n                }\n            }\n\n        }\n\n\n        if (ec) {\n            Base::lampLog::getInstance().log(\"ec: \" + ec.message(), Base::lampLog::ERROR, true,\n                                             Base::lampLog::LMP_NOFILEDROP);\n        }\n}\n\n\nLamp::Core::FS::lampReturn\nLamp::Core::FS::lampIO::downloadFile(Lamp::Core::FS::lampString url, Lamp::Core::FS::lampString output_filename) {\n    return Lamp::Core::FS::lampReturn(0, Base::lampTypes::lampString());\n}\n\nLamp::Core::lampReturn Lamp::Core::FS::lampIO::emptyFolder(Lamp::Core::lampString Path, Lamp::Core::lampString SpecificExtension) {\n    std::filesystem::create_directories(Path);\n    try {\n        if (SpecificExtension != \"\") {\n            for (const auto &entry: std::filesystem::directory_iterator(std::filesystem::path(Path))) {\n                std::filesystem::remove_all(entry.path());\n            }\n        }\n\n        for (const auto &entry: std::filesystem::directory_iterator(std::filesystem::path(Path))) {\n            if (std::regex_match(entry.path().filename().string(), std::regex(\"^.*\\\\.(\" + SpecificExtension + \")$\"))) {\n                std::filesystem::remove_all(entry.path());\n            }\n        }\n        return Lamp::Core::FS::lampReturn(1, Base::lampTypes::lampString());\n    }catch(std::exception e){\n        return Lamp::Core::FS::lampReturn(0, Base::lampTypes::lampString());\n    }\n\n}\n\n\n"
  },
  {
    "path": "Lampray/Filesystem/lampShare.cpp",
    "content": "//\n// Created by charles on 07/10/23.\n//\n#include <lz4hc.h>\n#include <climits>\n\n#include \"lampFS.h\"\n#include \"../../third-party/nfd/include/nfd.h\"\n\n#include \"../Control/lampGames.h\"\n#include \"bit7zlibrary.hpp\"\n#include \"bitfilecompressor.hpp\"\n#include \"bitarchivereader.hpp\"\n#include \"../Control/lampControl.h\"\n\nbool Lamp::Core::FS::lampShare::compressFile(const std::string& inputFile, const std::string& outputFile) {\n    // Open the input file for reading\n    std::ifstream inFile(inputFile, std::ios::binary);\n    if (!inFile) {\n        std::cerr << \"Error opening input file: \" << inputFile << std::endl;\n        return false;\n    }\n\n    // Open the output file for writing\n    std::ofstream outFile(outputFile, std::ios::binary);\n    if (!outFile) {\n        std::cerr << \"Error opening output file: \" << outputFile << std::endl;\n        return false;\n    }\n\n    // Read the input file into a buffer\n    inFile.seekg(0, std::ios::end);\n    std::streampos fileSize = inFile.tellg();\n    inFile.seekg(0, std::ios::beg);\n\n    std::vector<char> inputBuffer(fileSize);\n    inFile.read(inputBuffer.data(), fileSize);\n\n    // Compress the input buffer using LZ4HC\n    int maxCompressedSize = LZ4_compressBound(fileSize);\n    std::vector<char> compressedBuffer(maxCompressedSize);\n\n    int compressedSize = LZ4_compress_HC(inputBuffer.data(), compressedBuffer.data(), fileSize, maxCompressedSize, LZ4HC_CLEVEL_MAX);\n\n    if (compressedSize <= 0) {\n        std::cerr << \"Compression failed.\" << std::endl;\n        return false;\n    }\n\n    // Write the compressed data to the output file\n    outFile.write(compressedBuffer.data(), compressedSize);\n\n    return true;\n}\nbool Lamp::Core::FS::lampShare::decompressFile(const std::string& inputFile, const std::string& outputFile, std::uintmax_t volumeSize) {    // Open the input file for reading\n    std::ifstream inFile(inputFile, std::ios::binary);\n    if (!inFile) {\n        std::cerr << \"Error opening input file: \" << inputFile << std::endl;\n        return false;\n    }\n\n    // Open the output file for writing\n    std::ofstream outFile(outputFile, std::ios::binary);\n    if (!outFile) {\n        std::cerr << \"Error opening output file: \" << outputFile << std::endl;\n        return false;\n    }\n\n    // Read the compressed input file into a buffer\n    inFile.seekg(0, std::ios::end);\n    std::streampos compressedSize = inFile.tellg();\n\n    if (compressedSize > 0) {\n        compressedSize -= 1; // Drop the last byte from the size\n    }\n\n    inFile.seekg(0, std::ios::beg);\n\n\n    std::vector<char> compressedBuffer(compressedSize);\n    inFile.read(compressedBuffer.data(), compressedSize);\n\n    // Determine the decompressed size based on your knowledge of the original size\n    // If you don't know the original size, you may need to store it somewhere or use a different compression library.\n    // For this example, we assume you know the original size.\n    std::streampos decompressedSize = volumeSize;\n\n    // Allocate a buffer for the decompressed data\n    std::vector<char> decompressedBuffer(decompressedSize);\n\n    // Perform the decompression\n    int result = LZ4_decompress_safe(compressedBuffer.data(), decompressedBuffer.data(), compressedSize, decompressedSize);\n\n    if (result <= 0) {\n        std::cerr << \"Decompression failed.\" << std::endl;\n        return false;\n    }\n\n    // Write the decompressed data to the output file\n    outFile.write(decompressedBuffer.data(), decompressedSize);\n    return true;\n}\n\nvoid Lamp::Core::FS::lampShare::InsertXMLintoFile(const std::string& filename, const pugi::xml_node& metadata) {\n    // Open the file for reading and writing\n    std::fstream file(filename, std::ios::in | std::ios::out);\n    if (!file.is_open()) {\n        std::cerr << \"Error opening file: \" << filename << std::endl;\n        return;\n    }\n\n    // Create a string containing the XML data\n    std::stringstream xmlStream;\n    metadata.print(xmlStream);\n\n    // Read the existing content\n    std::string existingContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());\n\n    // Trim trailing newline character from existing content if present\n    if (!existingContent.empty() && existingContent.back() == '\\n') {\n        existingContent.pop_back();\n    }\n\n    // Close the file\n    file.close();\n\n    // Reopen the file for writing (truncate mode)\n    file.open(filename, std::ios::out | std::ios::trunc);\n    if (!file.is_open()) {\n        std::cerr << \"Error opening file for writing: \" << filename << std::endl;\n        return;\n    }\n\n    // Insert the XML data at the start of the file\n    file << xmlStream.str() << existingContent;\n}\nvoid Lamp::Core::FS::lampShare::ExtractXMLfromFile(const std::string& filename, std::string& content) {\n    // Open the file for reading\n    std::ifstream inputFile(filename);\n    if (!inputFile.is_open()) {\n        std::cerr << \"Error opening file: \" << filename << std::endl;\n        return;\n    }\n\n    // Open a temporary file for writing\n    std::string tempFilename = filename + \".temp\";\n    std::ofstream tempFile(tempFilename);\n    if (!tempFile.is_open()) {\n        std::cerr << \"Error opening temporary file.\" << std::endl;\n        inputFile.close();\n        return;\n    }\n\n    // Read and process the file line by line\n    std::string line;\n    std::string xmlContent;\n    bool inXmlSection = false;\n\n    while (std::getline(inputFile, line)) {\n        if (line == \"<metadata>\") {\n            inXmlSection = true;\n            xmlContent += line + \"\\n\"; // Include the \"<root>\" tag\n        } else if (inXmlSection) {\n            xmlContent += line + \"\\n\";\n\n            if (line == \"</metadata>\") {\n                inXmlSection = false;\n            }\n        } else {\n            // Write non-XML lines to the temporary file\n            tempFile << line << \"\\n\";\n        }\n    }\n\n    // Close both input and temporary files\n    inputFile.close();\n    tempFile.close();\n\n    // Rename the temporary file to replace the original file\n    if (std::rename(tempFilename.c_str(), filename.c_str()) != 0) {\n        std::cerr << \"Error renaming temporary file.\" << std::endl;\n        return;\n    }\n\n    // Load the extracted XML content into pugixml\n    pugi::xml_document doc;\n    pugi::xml_parse_result result = doc.load_string(xmlContent.c_str());\n    if (result) {\n        content = xmlContent; // The entire extracted content is the XML data\n    } else {\n        std::cerr << \"Error parsing extracted XML content.\" << std::endl;\n    }\n}\n\nvoid Lamp::Core::FS::lampShare::importProfile() {\n    std::thread([] {\n    Lamp::Core::lampControl::getInstance().inDeployment = true;\n    Lamp::Core::lampControl::getInstance().deploymentStageTitle = \"Importing Profile\";\n    Lamp::Core::lampControl::getInstance().deplopmentTracker = {0,11};\n    bit7z::Bit7zLibrary lib{Lamp::Core::lampConfig::getInstance().bit7zLibraryLocation};\n    std::filesystem::create_directories(\"import/\");\n    nfdchar_t *outPath = NULL;\n    nfdresult_t result = NFD_OpenDialog( \"lampProfile\", NULL, &outPath );\n    if ( result == NFD_OKAY )\n    {\n        puts(outPath);\n    }\n    else if ( result == NFD_CANCEL )\n    {\n        Lamp::Core::lampControl::getInstance().inDeployment = false;\n        return;\n    }\n    else\n    {\n        Lamp::Core::lampControl::getInstance().inDeployment = false;\n        return;\n    }\n    Lamp::Core::lampControl::getInstance().deplopmentTracker = {1,11};\n    std::filesystem::path sourcePath = outPath;  // Replace with your source file path\n    std::filesystem::path destinationPath = \"import/\";      // Replace with your destination folder path\n\n    try {\n        // Check if the source file exists\n        if (std::filesystem::exists(sourcePath)) {\n            // Move the file to the destination folder\n            std::filesystem::rename(sourcePath, destinationPath / sourcePath.filename());\n        } else {\n            Lamp::Core::lampControl::getInstance().inDeployment = false;\n            return;\n        }\n    } catch (const std::filesystem::filesystem_error& e) {\n        Lamp::Core::lampControl::getInstance().inDeployment = false;\n        return;\n    }\n\n    Lamp::Core::lampControl::getInstance().deplopmentTracker = {2,11};\n\n    try {\n        std::string content;\n        Lamp::Core::FS::lampShare::ExtractXMLfromFile((destinationPath / sourcePath.filename()), content);\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {3,11};\n        pugi::xml_document dcl;\n        dcl.load_string(content.c_str());\n\n        // Find the \"Profile\" node\n        pugi::xml_node profileNode = dcl.child(\"metadata\").child(\"Profile\");\n\n        // Check if the \"Profile\" node exists\n        if (!profileNode) {\n            Lamp::Core::lampControl::getInstance().inDeployment = false;\n            return;\n        }\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {4,11};\n        // Find the \"Volume\" attribute within the \"Profile\" node\n        pugi::xml_attribute volumeAttr = profileNode.attribute(\"Volume\");\n\n        // Check if the \"Volume\" attribute exists\n        if (!volumeAttr) {\n            Lamp::Core::lampControl::getInstance().inDeployment = false;\n            return;\n        }\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {5,11};\n        std::uintmax_t volumeValue;\n        try {\n            volumeValue = std::stoull(volumeAttr.value());\n        } catch (const std::invalid_argument &e) {\n            std::cerr << \"Failed to convert 'Volume' attribute value to std::uintmax_t: \" << e.what() << std::endl;\n        }\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {6,11};\n        decompressFile((destinationPath / sourcePath.filename()), \"import/lampPackage\", volumeValue);\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {7,11};\n        bit7z::BitArchiveReader reader{lib, \"import/lampPackage\", bit7z::BitFormat::SevenZip};\n        reader.test();\n        reader.extract(Lamp::Core::lampConfig::getInstance().archiveDataPath +\n                       Lamp::Games::getInstance().currentGame->Ident().ReadableName + \"/\");\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {8,11};\n        std::vector<Base::lampMod::Mod *> newList;\n        for (pugi::xml_node modNode = profileNode.child(\"Mod\"); modNode; modNode = modNode.next_sibling(\n                \"Mod\")) {\n            std::string archivePath = modNode.attribute(\"ArchivePath\").as_string();\n            int modType = modNode.attribute(\"modType\").as_int();\n            bool enabled = modNode.attribute(\"enabled\").as_bool();\n\n            Base::lampMod::Mod *temp = new Base::lampMod::Mod{archivePath, modType, enabled};\n\n            newList.emplace_back(temp);\n        }\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {9,11};\n\n\n        // Add new profile to profile list.\n        Lamp::Core::Base::lampMod::Profile::addValue(Lamp::Games::getInstance().currentGame->KeyInfo()[\"ProfileList\"],\n                                                     profileNode.attribute(\"Name\").value());\n        // Overwrite the current modlist with the new one.\n        Lamp::Games::getInstance().currentGame->getModList() = newList;\n        // save it under the profile name\n        FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand,\n                                Lamp::Games::getInstance().currentGame->getModList(),\n                                profileNode.attribute(\"Name\").value());\n        // reload that new profile name\n        Lamp::Games::getInstance().currentGame->getModList() = FS::lampIO::loadModList(\n                Lamp::Games::getInstance().currentGame->Ident().ShortHand, profileNode.attribute(\"Name\").value());\n        // set the current profile to the new one.\n        Lamp::Games::getInstance().currentProfile = profileNode.attribute(\"Name\").value();\n        // Save our profile list.\n        FS::lampIO::saveKeyData(\"ProfileList\", Lamp::Games::getInstance().currentGame->KeyInfo()[\"ProfileList\"],\n                                Lamp::Games::getInstance().currentGame->Ident().ShortHand);\n\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {10,11};\n        try {\n            if (std::filesystem::exists(\"import/lampPackage\")) {\n                std::filesystem::remove(\"import/lampPackage\");\n                std::filesystem::remove(outPath);\n            }\n        } catch (const std::filesystem::filesystem_error &e) {\n        }\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {11,11};\n        Lamp::Core::lampControl::getInstance().inDeployment = false;\n    }catch (std::exception ex){\n\n    }\n        Lamp::Core::lampControl::getInstance().inDeployment = false;\n    }).detach();\n}\nvoid Lamp::Core::FS::lampShare::exportProfile(std::string profileNameS) {\n    std::thread([&profileNameS] {\n    try {\n        std::string profileName = profileNameS;\n        Lamp::Core::lampControl::getInstance().inDeployment = true;\n        Lamp::Core::lampControl::getInstance().deploymentStageTitle = \"Creating \"+profileName +\".lampProfile\";\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {0,3};\n        bit7z::Bit7zLibrary lib{Lamp::Core::lampConfig::getInstance().bit7zLibraryLocation};\n        std::vector<Lamp::Core::Base::lampMod::Mod *> TempModList = Lamp::Core::FS::lampIO::loadModList(\n                Lamp::Games::getInstance().currentGame->Ident().ShortHand, profileName);\n        std::map<std::string, std::string> files_map = {};\n        for (const Base::lampMod::Mod *mod: TempModList) {\n            if (mod->enabled) {\n                files_map[mod->ArchivePath] = std::filesystem::path(mod->ArchivePath).filename().string();\n            }\n        }\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {1,3};\n        bit7z::BitFileCompressor compressorSevenZip{lib, bit7z::BitFormat::SevenZip};\n        compressorSevenZip.compress(files_map, \"lampPackage\");\n        compressorSevenZip.setCompressionLevel(bit7z::BitCompressionLevel::Ultra);\n        compressFile(\"lampPackage\",  profileName + \".lampProfile\");\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {2,3};\n        // Create the profile metadata\n        pugi::xml_document metadataDoc;\n        pugi::xml_node root = metadataDoc.append_child(\"metadata\");\n\n        pugi::xml_node profileNewNode = root.append_child(\"Profile\");\n        profileNewNode.append_attribute(\"Name\").set_value(profileName.c_str());\n        for (const Base::lampMod::Mod *mod: TempModList) {\n            if (mod->enabled) {\n                mod->serialize(profileNewNode);\n            }\n        }\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {3,3};\n        std::uintmax_t fileSize = std::filesystem::file_size(\"lampPackage\");\n        profileNewNode.append_attribute(\"Volume\").set_value(fileSize);\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {3,3};\n        // Delete lampPackage\n        try {\n            if (std::filesystem::exists(\"lampPackage\")) {\n                std::filesystem::remove(\"lampPackage\");\n            }\n        } catch (const std::filesystem::filesystem_error &e) {\n        }\n        InsertXMLintoFile( profileName + \".lampProfile\", root);\n\n        Lamp::Core::lampControl::getInstance().inDeployment = false;\n        Lamp::Core::Base::lampLog::getInstance().log(\"Profile Exported: \" + absolute(fs::path( profileName + \".lampProfile\")).string(),Base::lampLog::LOG,true);\n    }catch(std::exception e){\n\n    }\n    }).detach();\n}\n"
  },
  {
    "path": "Lampray/Filesystem/lampTrack.cpp",
    "content": "//\n// Created by charles on 10/10/23.\n//\n#include <algorithm>\n#include <filesystem>\n#include \"lampFS.h\"\nstd::string Lamp::Core::FS::lampTrack::getHash(std::filesystem::path filePath) {\n    std::ifstream file(filePath, std::ios::binary);\n    if (!file) {\n        throw std::runtime_error(\"Failed to open file\");\n    }\n\n    const size_t bufferSize = 4096;\n    char buffer[bufferSize];\n\n    unsigned int hash = 0xFFFFFFFF; // Initialize CRC32 with all 1s\n\n    while (file.good()) {\n        file.read(buffer, bufferSize);\n        const size_t bytesRead = static_cast<size_t>(file.gcount());\n        hash = crc32(buffer, bytesRead);\n    }\n\n    // Convert the numeric hash to a hexadecimal string\n    std::stringstream stream;\n    stream << std::hex << std::uppercase << std::setfill('0') << std::setw(8) << hash;\n    return stream.str();\n}\n\n\nbool Lamp::Core::FS::lampTrack::doesFilenameExist(const std::string &game, const std::string &filename) {\n    for (const auto& file : getTrackedFiles()) {\n        if (file.game == game && file.gameLocation == filename) {\n            return true; // Found a match\n        }\n    }\n    return false; // No match found\n}\n\nbool Lamp::Core::FS::lampTrack::doesHashExist(const std::string &game, const std::string &hash) {\n    for (const auto& file : getTrackedFiles()) {\n        if (file.game == game && file.fileHash == hash) {\n            return true; // Found a match\n        }\n    }\n    return false; // No match found\n}\n\n\n void Lamp::Core::FS::lampTrack::saveTracker(const std::string& filename, const std::vector<trackedFile>& files) {\n    pugi::xml_document doc;\n    pugi::xml_node root = doc.append_child(\"Tracker\");\n\n    for (const auto& file : files) {\n        pugi::xml_node fileNode = root.append_child(\"TrackedFile\");\n        fileNode.append_child(\"Game\").text() = file.game.c_str();\n        fileNode.append_child(\"GameLocation\").text() = file.gameLocation.c_str();\n        fileNode.append_child(\"FileHash\").text() = file.fileHash.c_str();\n        fileNode.append_child(\"Type\").text() = static_cast<int>(file.type);\n    }\n\n    if (doc.save_file(filename.c_str())) {\n       Lamp::Core::Base::lampLog::getInstance().log(\"Tracker data saved to \" + filename, Base::lampLog::LOG);\n    } else {\n        Lamp::Core::Base::lampLog::getInstance().log(\"Error saving tracker data to \" + filename, Lamp::Core::Base::lampLog::ERROR);\n    }\n}\n\nvoid Lamp::Core::FS::lampTrack::loadTracker(const std::string& filename) {\n    getTrackedFiles().clear();\n    pugi::xml_document doc;\n    if (doc.load_file(filename.c_str())) {\n        pugi::xml_node root = doc.child(\"Tracker\");\n        getTrackedFiles().clear(); // Clear existing data\n\n        for (pugi::xml_node fileNode = root.child(\"TrackedFile\"); fileNode; fileNode = fileNode.next_sibling(\"TrackedFile\")) {\n            trackedFile file;\n            file.game = fileNode.child_value(\"Game\");\n            file.gameLocation = fileNode.child_value(\"GameLocation\");\n            file.fileHash = fileNode.child_value(\"FileHash\");\n            file.type = static_cast<trackedFileType>(fileNode.child(\"Type\").text().as_int());\n            getTrackedFiles().push_back(file);\n        }\n\n        Lamp::Core::Base::lampLog::getInstance().log(\"Tracker data loaded from \" + filename, Base::lampLog::LOG);\n    } else {\n        Lamp::Core::Base::lampLog::getInstance().log(\"Error loading tracker data from \" + filename, Lamp::Core::Base::lampLog::ERROR);\n    }\n}\n\nLamp::Core::lampReturn\nLamp::Core::FS::lampTrack::handleFile(Lamp::Core::FS::lampTrack::handleFileDescriptor descriptor) {\n    if(getTrackedFiles().empty()){\n        loadTracker(lampConfig::getInstance().ConfigDataPath+\"lampTrack.mdf\");\n    }\n\n    switch (descriptor.handlerMode) {\n        case handleFileDescriptor::direct:\n            break;\n        case handleFileDescriptor::skipExisting:\n            descriptor.copyOperationOptions = descriptor.copyOperationOptions | std::filesystem::copy_options::skip_existing;\n            break;\n        case handleFileDescriptor::updateExisting:\n            descriptor.copyOperationOptions = descriptor.copyOperationOptions | std::filesystem::copy_options::update_existing;\n            break;\n    }\n\n\n    switch (descriptor.handlerOperation) {\n        case handleFileDescriptor::copyFile:\n            if(trackOperation(descriptor.filePath,descriptor.target,descriptor.gameFullName)) {\n                std::filesystem::copy_file(descriptor.filePath,descriptor.target, descriptor.copyOperationOptions);\n            }\n            break;\n        case handleFileDescriptor::copyFolder:\n            copyAndOperate(descriptor.filePath, descriptor.target,descriptor.gameFullName);\n            break;\n        case handleFileDescriptor::copyFilesIgnoreExt:\n            recursiveCopyWithIgnore(descriptor.filePath, descriptor.target,std::vector<std::string>{descriptor.extName},descriptor.copyOperationOptions,descriptor.gameFullName);\n            break;\n        case handleFileDescriptor::copyOnlyExt:\n            std::filesystem::create_directories(descriptor.target);\n            copyExtensionWithFileTypeIgnore(descriptor.filePath, descriptor.target, descriptor.extName, descriptor.copyOperationOptions,descriptor.gameFullName);\n            break;\n    }\n\n\n    return {0,\"\"};\n\n}\n\nvoid Lamp::Core::FS::lampTrack::recursiveCopyWithIgnore(const std::filesystem::path &source,\n                                                        const std::filesystem::path &destination,\n                                                        const std::vector<std::string> &ignoreFolders, std::filesystem::copy_options options, std::string game) {\n    for (const auto &entry: std::filesystem::directory_iterator(source)) {\n        if (entry.is_directory()) {\n            if (std::find(ignoreFolders.begin(), ignoreFolders.end(), entry.path().filename()) ==\n                ignoreFolders.end()) {\n                std::filesystem::create_directories(destination / entry.path().filename());\n                recursiveCopyWithIgnore(entry.path(), destination / entry.path().filename(), ignoreFolders, options, game);\n            }\n        } else if (entry.is_regular_file()) {\n            try {\n\n\n\n                if(trackOperation(entry.path(),destination / entry.path().filename(),game)) {\n                    std::filesystem::copy_file(entry.path(), destination / entry.path().filename(),\n                                               options);\n                }\n            }\n            catch (const std::filesystem::filesystem_error &e) {\n                Lamp::Core::Base::lampLog::getInstance().log(\"Error copying file: \" + (std::string)e.what(), Lamp::Core::Base::lampLog::ERROR);\n            }\n        }\n    }\n\n}\n\nvoid Lamp::Core::FS::lampTrack::copyExtensionWithFileTypeIgnore(const std::filesystem::path &sourceDir,\n                                                                const std::filesystem::path &destDir,\n                                                                std::string nonIgnoredExtension, std::filesystem::copy_options options, std::string game) {\n    for (const auto &entry: std::filesystem::directory_iterator(sourceDir)) {\n        const std::filesystem::path &sourcePath = entry.path();\n        const std::filesystem::path destPath = destDir / sourcePath.filename();\n        if (std::filesystem::is_regular_file(sourcePath)) {\n            try {\n                std::string extension = sourcePath.extension().string();\n                if ((extension == nonIgnoredExtension) && trackOperation(sourcePath,destPath,game)) {\n                    std::filesystem::copy_file(sourcePath, destPath, options);\n                }\n            } catch (const std::filesystem::filesystem_error &e) {\n            }\n\n\n        } else if (std::filesystem::is_directory(sourcePath)) {\n            std::filesystem::create_directory(destPath);\n            copyExtensionWithFileTypeIgnore(sourcePath, destPath, nonIgnoredExtension, options, game);\n        }\n    }\n}\n\nLamp::Core::lampReturn\nLamp::Core::FS::lampTrack::trackOperation(const std::string &file, const std::string &destination, const std::string &game) {\n    const std::filesystem::path &sourcePath = file;\n    const std::filesystem::path destPath = destination;\n\n\n    std::string fileHash = getHash(sourcePath);\n\n    if (std::filesystem::exists(destPath)) {\n\n        if(doesHashExist(game, fileHash) && !doesFilenameExist(game,destPath)){\n            return Base::lampLog::getInstance().pLog({0, sourcePath.filename().string() + \" hash exists but the file does not. Skipping.\"}, Base::lampLog::ERROR,true);\n        }\n\n        if(doesHashExist(game, fileHash) && doesFilenameExist(game,destPath)){\n            return Base::lampLog::getInstance().pLog({0, sourcePath.filename().string() + \" is up to date.\"});\n        }\n\n        if(!doesHashExist(game, fileHash) && doesFilenameExist(game,destPath)){\n            for (auto& file : getTrackedFiles()) {\n                if (file.game == game && file.gameLocation == destPath) {\n                    if(file.type == trackedFileType::GameFile)\n                    file.fileHash = fileHash;\n\n                    return Base::lampLog::getInstance().pLog({1, sourcePath.filename().string() + \" Was outdated updating hash and updating file.\"});\n                }\n            }\n        }\n\n        trackedFile newTrackedFile;\n        newTrackedFile.game = game;\n        newTrackedFile.fileHash = fileHash;\n        newTrackedFile.gameLocation = destPath;\n        newTrackedFile.type = trackedFileType::GameFile;\n\n        getTrackedFiles().push_back(newTrackedFile);\n        saveTracker(lampConfig::getInstance().ConfigDataPath+\"lampTrack.mdf\", getTrackedFiles());\n        if(!std::filesystem::exists(lampConfig::getInstance().archiveDataPath+game+\"/GameFiles/\"+destPath.filename().string())) {\n            std::filesystem::copy(destPath, lampConfig::getInstance().archiveDataPath + game + \"/GameFiles/\");\n        }\n        return Base::lampLog::getInstance().pLog({1, sourcePath.filename().string() + \" tracking created.\"});\n\n    } else {\n        trackedFile newTrackedFile;\n        newTrackedFile.game = game;\n        newTrackedFile.fileHash = fileHash;\n        newTrackedFile.gameLocation = destPath;\n        newTrackedFile.type = trackedFileType::ModFile;\n        getTrackedFiles().push_back(newTrackedFile);\n        saveTracker(lampConfig::getInstance().ConfigDataPath+\"lampTrack.mdf\", getTrackedFiles());\n        return Base::lampLog::getInstance().pLog({1, sourcePath.filename().string() + \" ModFile created, game did not contain file.\"});\n    }\n\n}\n\nvoid Lamp::Core::FS::lampTrack::copyAndOperate(const std::filesystem::path &source,\n                                               const std::filesystem::path &destination, std::string game) {\n    if (!std::filesystem::exists(source)) {\n        Lamp::Core::Base::lampLog::getInstance().log(\"Source does not exist: \" + (std::string)source, Lamp::Core::Base::lampLog::ERROR);\n        return;\n    }\n    if (std::filesystem::is_directory(source)) {\n        if (!std::filesystem::exists(destination)) {\n            std::filesystem::create_directories(destination);\n        }\n\n        for (const auto& entry : std::filesystem::directory_iterator(source)) {\n            const std::filesystem::path& sourceEntry = entry.path();\n            const std::filesystem::path destinationEntry = destination / sourceEntry.filename();\n\n            copyAndOperate(sourceEntry, destinationEntry, game);\n        }\n    } else if (std::filesystem::is_regular_file(source)) {\n        if (trackOperation(source, destination, game)) {\n            std::filesystem::copy_file(source, destination,\n                                       std::filesystem::copy_options::update_existing);\n        }\n    }\n}\n\nvoid Lamp::Core::FS::lampTrack::reset(std::string gameFullReadableName) {\n    auto temp = getTrackedFiles();\n    for (const auto& file : temp) {\n        if (file.game == gameFullReadableName) {\n            if (file.type == trackedFileType::ModFile) {\n                // Delete ModFile\n                deleteModFile(file.gameLocation);\n            } else if (file.type == trackedFileType::GameFile) {\n                // Replace GameFile with counterpart from GameFiles folder\n                replaceGameFile(file.gameLocation,\n                                lampConfig::getInstance().archiveDataPath + gameFullReadableName + \"/GameFiles/\");\n            }\n        }\n    }\n    saveTracker(lampConfig::getInstance().ConfigDataPath+\"lampTrack.mdf\", getTrackedFiles());\n}\n\nvoid Lamp::Core::FS::lampTrack::replaceGameFile(const std::filesystem::path &gameFilePath,\n                                                const std::filesystem::path &gameFilesFolder) {\n    if (std::filesystem::exists(gameFilePath) && std::filesystem::is_regular_file(gameFilePath)) {\n        std::filesystem::path gameFileName = gameFilePath.filename();\n        std::filesystem::path gameFilesPath = gameFilesFolder / gameFileName;\n\n        if (std::filesystem::exists(gameFilesPath) && std::filesystem::is_regular_file(gameFilesPath)) {\n            //std::filesystem::rename(gameFilesPath, gameFilePath);\n            std::filesystem::copy_file(gameFilesPath,gameFilePath, std::filesystem::copy_options::overwrite_existing);\n\n            Lamp::Core::Base::lampLog::getInstance().log(\"GameFile replaced: \" + (std::string)gameFilePath, Lamp::Core::Base::lampLog::LOG);\n            // Remove the processed file from the trackedFiles vector\n            getTrackedFiles().erase(\n                    std::remove_if(getTrackedFiles().begin(), getTrackedFiles().end(),\n                                   [&](const trackedFile& file) {\n                                       return file.gameLocation == gameFilePath;\n                                   }),\n                    getTrackedFiles().end());\n\n\n        } else {\n            Lamp::Core::Base::lampLog::getInstance().log(\"GameFile counterpart not found: \" + (std::string)gameFilesPath, Lamp::Core::Base::lampLog::ERROR);\n        }\n    } else {\n        Lamp::Core::Base::lampLog::getInstance().log(\"GameFile not found or is not a regular file: \" + (std::string)gameFilePath, Lamp::Core::Base::lampLog::ERROR);\n    }\n}\n\nvoid Lamp::Core::FS::lampTrack::deleteModFile(const std::filesystem::path &modFilePath) {\n    if (std::filesystem::exists(modFilePath) && std::filesystem::is_regular_file(modFilePath)) {\n        std::filesystem::remove(modFilePath);\n\n        getTrackedFiles().erase(\n                std::remove_if(getTrackedFiles().begin(), getTrackedFiles().end(),\n                               [&](const trackedFile& file) {\n                                   return file.gameLocation == modFilePath;\n                               }),\n                getTrackedFiles().end());\n\n        Lamp::Core::Base::lampLog::getInstance().log(\"ModFile deleted: \" + (std::string)modFilePath, Lamp::Core::Base::lampLog::LOG);\n    } else {\n        Lamp::Core::Base::lampLog::getInstance().log(\"ModFile not found or is not a regular file: \" + (std::string)modFilePath, Lamp::Core::Base::lampLog::LOG);\n    }\n}\n\n\n\n\n"
  },
  {
    "path": "Lampray/Filesystem/lampUpdate.cpp",
    "content": "//\n// Created by charles on 27/09/23.\n//\n#include \"lampFS.h\"\n\n#include <curl/curl.h>\n#include <string>\n#include <iostream>\n#include \"../../third-party/imgui/imgui.h\"\n\nvoid Lamp::Core::FS::lampUpdate::checkForUpdates() {\n    CURL* curl = curl_easy_init();\n    if (curl) {\n        std::string url = \"https://raw.githubusercontent.com/CHollingworth/Lampray/master/VERSION\";\n        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());\n        std::string response_data;\n        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);\n        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_data);\n        CURLcode res = curl_easy_perform(curl);\n        if (res != CURLE_OK) {\n            std::cerr << \"curl_easy_perform() failed: \" << curl_easy_strerror(res) << std::endl;\n        } else {\n            size_t pos = response_data.find('\\n');\n            if (pos != std::string::npos) {\n                // Find the position of the second newline character\n                size_t second_pos = response_data.find('\\n', pos + 1);\n                if (second_pos != std::string::npos) {\n                    // Extract the second line of the response\n                    std::string second_line = response_data.substr(pos + 1, second_pos - pos - 1);\n                    if (versionNumber != second_line) {\n                        versionNumber = \"Update Available!\";\n                    }\n                }\n            }\n        }\n        curl_easy_cleanup(curl);\n    }\n}\n\nvoid Lamp::Core::FS::lampUpdate::getExpression() {\n    auto style = ImGui::GetStyle();\n    float buttonWidth1 = ImGui::CalcTextSize((\"v\"+versionNumber).c_str()).x + style.FramePadding.x * 2.f;\n    float widthNeeded = buttonWidth1;\n    ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - widthNeeded);\n\n\n    if(versionNumber == \"Update Available!\") {\n        if (ImGui::Button((versionNumber).c_str())) {\n            const char* url = \"https://github.com/CHollingworth/Lampray/releases/latest\";\n            std::string openCommand = \"xdg-open \" + std::string(url);\n            system(openCommand.c_str());\n        }\n    }else{\n        ImGui::Text(\"%s\", (\"v\"+versionNumber).c_str());\n    }\n}\n"
  },
  {
    "path": "Lampray/Lang/lampLang.h",
    "content": "//\n// Created by charles on 18/12/23.\n//\n\n#ifndef LAMPRAY_LAMPLANG_H\n#define LAMPRAY_LAMPLANG_H\n\n#include <filesystem>\n#include <string>\n#include <map>\n#include <pugixml.hpp>\n#include <unordered_set>\n#include \"../Control/lampConfig.h\"\n#include \"../Base/lampBase.h\"\n\nnamespace Lamp {\n    namespace Core {\n\n        class lampLang {\n        public:\n\n            static lampLang& getInstance()\n            {\n                static lampLang instance;\n                return instance;\n            }\n\n            lampLang(lampLang const&) = delete;\n            void operator=(lampLang const&)  = delete;\n\n\n            struct LanguageContainer{\n            private:\n                std::string LanguageName;\n                std::map<std::string,std::string> LanguageMap;\n                std::unordered_set<std::string> KnownKeys;\n            public:\n                Base::lampTypes::lampString S(std::string S){\n\n                    if (KnownKeys.find(S) != KnownKeys.end()) {\n                        return LanguageMap[S];\n                    } else {\n                        auto it = LanguageMap.find(S);\n                        if (it != LanguageMap.end()) {\n                            KnownKeys.insert(S);\n                            return LanguageMap[S];\n                        } else {\n                            return \"[?]\"+S;\n                        }\n                    }\n\n                }\n\n                Base::lampTypes::lampReturn build(const std::filesystem::path& filePath){\n\n                    if (!std::filesystem::exists(filePath)) {\n                        return {false, \"File not found: \" + filePath.string()};\n                    }\n\n                    // Load the XML document\n                    std::string xmlPath = filePath;\n\n                    pugi::xml_document doc;\n\n                    if (doc.load_file(xmlPath.c_str())) {\n\n                    }\n\n                    pugi::xml_node langNode = doc.child(\"LamprayLang\");\n                    if (langNode) {\n                        LanguageName = langNode.attribute(\"name\").value();\n                    } else {\n                        return {false, \"Failed to load language. No Name.\"};\n                    }\n\n                    for (pugi::xml_node node = langNode.child(\"LangNode\"); node; node = node.next_sibling(\"LangNode\")) {\n                        std::string key = node.attribute(\"key\").value();\n                        std::string value = node.child_value();\n                        LanguageMap[key] = value;\n                    }\n\n                    return {true, \"Language Loaded.\"};\n                }\n\n            };\n\n\n            static Base::lampTypes::lampString localizedString(Base::lampTypes::lampString key){\n                return Lamp::Core::lampLang::getInstance().CurrentLanguage.S(key);\n            }\n\n            static Base::lampTypes::lampString LS(Base::lampTypes::lampString k){\n                return lampLang::localizedString(k);\n            }\n\n            LanguageContainer CurrentLanguage;\n\n            std::filesystem::path createEnglishUK(){\n                pugi::xml_document doc;\n                auto root = doc.append_child(\"LamprayLang\");\n                root.append_attribute(\"name\").set_value(\"English (UK)\");\n\n                auto addLangNode = [&](const char* key, const char* text) {\n                    auto node = root.append_child(\"LangNode\");\n                    node.append_attribute(\"key\").set_value(key);\n                    node.text().set(text);\n                };\n\n                addLangNode(\"LAMPRAY_LONGNAME\", \"Linux Application Modding Platform\");\n                addLangNode(\"LAMPRAY_LICENSE\", R\"(\nThis is free and unencumbered software released into the public domain.\n\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\n\nFor more information, please refer to <https://unlicense.org>)\");\n                addLangNode(\"LAMPRAY_AKNOWL\", \"I have read and understood the text above.\");\n                addLangNode(\"LAMPRAY_DRAGANDDROP\", \"Drag archives onto this window to start adding mods to your game!\");\n                addLangNode(\"LAMPRAY_DEPLOY\", \"Deploy\");\n                addLangNode(\"LAMPRAY_RESET\", \"Reset\");\n                addLangNode(\"LAMPRAY_CHECK\", \"Checks\");\n                addLangNode(\"LAMPRAY_STARTTEXT\", \"Start Deployment?\");\n                addLangNode(\"LAMPRAY_START\", \"Start\");\n                addLangNode(\"LAMPRAY_GOBACK\", \"Back\");\n                addLangNode(\"LAMPRAY_GAMEPICK\", \"Game Selection\");\n                addLangNode(\"LAMPRAY_GAMECONFIG\", \"Game Configuration\");\n                addLangNode(\"LAMPRAY_PROFSELECT\", \"Profile Selection\");\n                addLangNode(\"LAMPRAY_PROFLOAD\", \"Load Profile\");\n                addLangNode(\"LAMPRAY_PROFDEL\", \"Delete Profile\");\n                addLangNode(\"LAMPRAY_PROFCRE\", \"Create New Profile\");\n                addLangNode(\"LAMPRAY_PROFCRES\", \"Create\");\n                addLangNode(\"LAMPRAY_PROFEXP\", \"Export Profile\");\n                addLangNode(\"LAMPRAY_PROFIMP\", \"Import Profile\");\n                addLangNode(\"LAMPRAY_PROFFRM\", \"Copy From\");\n                addLangNode(\"LAMPRAY_PROFNON\", \"None\");\n                addLangNode(\"LAMPRAY_PROFNM\", \"New Profile Name\");\n                addLangNode(\"LAMPRAY_CUSTOM\", \"Customise Lampray\");\n                addLangNode(\"LAMPRAY_ABT\", \"About\");\n                addLangNode(\"LAMPRAY_ABT1\", \"Created by\");\n                addLangNode(\"LAMPRAY_QUIT\", \"Quit\");\n                addLangNode(\"LAMPRAY_MODLIST_ENABLE\", \"Enabled\");\n                addLangNode(\"LAMPRAY_MODLIST_DISABLE\", \"Disabled\");\n                addLangNode(\"LAMPRAY_MODLIST_EXPND\", \"Expand\");\n                addLangNode(\"LAMPRAY_MODLIST_COLL\", \"Collapse\");\n                addLangNode(\"LAMPRAY_MODLIST_REMOVE_MOD\", \"Remove Mod\");\n                addLangNode(\"LAMPRAY_MODLIST_REMOVE_SEP\", \"Remove\");\n                addLangNode(\"LAMPRAY_MODLIST_MV_TOP\", \"Move to Top\");\n                addLangNode(\"LAMPRAY_MODLIST_MV_BTTM\", \"Move to Bottom\");\n                addLangNode(\"LAMPRAY_MODLIST_RENAME\", \"Rename Separator\");\n                addLangNode(\"LAMPRAY_MODLIST_CREATE_SEP\", \"Create Separator\");\n                addLangNode(\"LAMPRAY_MODLIST_SEARCH\", \"Type here to search your mods...\");\n                addLangNode(\"LAMPRAY_MODLIST_UP\", \"Up\");\n                addLangNode(\"LAMPRAY_MODLIST_DOWN\", \"Down\");\n                addLangNode(\"LAMPRAY_MODLIST_WARN\", \"Only disabled mods can be removed.\");\n                addLangNode(\"LAMPRAY_MODLIST_CONFRIMDEL\", \"Confirm Deletion\");\n                addLangNode(\"LAMPRAY_MODLIST_WARN_FINAL\", R\"(?\n\nThis action cannot be undone.)\");\n                addLangNode(\"LAMPRAY_MODLIST_WARN_SURE\", \"Are you sure you want to delete: \");\n                addLangNode(\"LAMPRAY_MODLIST_DELETE\", \"Delete\");\n                addLangNode(\"LAMPRAY_MODLIST_TITLE_ENABLED\", \"Enabled\");\n                addLangNode(\"LAMPRAY_MODLIST_TITLE_MODNAME\", \"Mod Name\");\n                addLangNode(\"LAMPRAY_MODLIST_TITLE_MODTYPE\", \"Mod Type\");\n                addLangNode(\"LAMPRAY_MODLIST_TITLE_LASTUPDATE\", \"Last Updated\");\n                addLangNode(\"LAMPRAY_MODLIST_TITLE_ORDER\", \"Load Order\");\n                addLangNode(\"LAMPRAY_MODLIST_TITLE_REMOVEMOD\", \"Remove Mod\");\n                addLangNode(\"LAMPRAY_UPDATE_CHECK\", \"Check for Updates\");\n                addLangNode(\"LAMPRAY_UPDATE_RULES\", \"Update Rules\");\n                addLangNode(\"LAMPRAY_UPDATE_RULES1\", \"Check for updates at startup\");\n                addLangNode(\"LAMPRAY_CUSTOM_LANG\", \"Language\");\n                addLangNode(\"LAMPRAY_CUSTOM_COLOUR\", \"Colours\");\n                addLangNode(\"LAMPRAY_CUSTOM_FONT\", \"Font Size\");\n                addLangNode(\"LAMPRAY_CUSTOM_SV\", \"Save\");\n                addLangNode(\"LAMPRAY_SELECT_PATH\", \"Select Path\");\n                addLangNode(\"LAMPRAY_ERROR_7Z\", \"Failed to find 7z.so! Many actions, such as deployment, will not function correctly. See the wiki for more information.\");\n                addLangNode(\"LAMPRAY_WARN_GAME_PATH\", \" directories are not set. Deployment will not work until you have set them in the Game Configuration menu.\");\n\n                auto baseDirectory = Lamp::Core::lampConfig::getInstance().baseDataPath + \"Language/\";\n                std::filesystem::create_directory(baseDirectory);\n                \n                std::filesystem::path path = baseDirectory + \"English (UK).xml\";\n                doc.save_file(path.c_str());\n                return path; \n            }\n\n        private:\n            lampLang(){};\n        };\n\n    } // Core\n} // Lamp\n\n#endif //LAMPRAY_LAMPLANG_H\n"
  },
  {
    "path": "Lampray/Menu/lampCustomise.h",
    "content": "//\n// Created by charles on 08/10/23.\n//\n\n#ifndef LAMP_LAMPCUSTOMISE_H\n#define LAMP_LAMPCUSTOMISE_H\n\n#include \"../../third-party/imgui/imgui.h\"\n#include \"../Base/lampBase.h\"\n#include \"../Filesystem/lampFS.h\"\n#include \"../../third-party/imgui/imgui_internal.h\"\n\nnamespace Lamp {\n    namespace Core {\n\n        class lampCustomise {\n        public:\n\n            static lampCustomise& getInstance()\n            {\n                static lampCustomise instance;\n                return instance;\n            }\n\n            lampCustomise(lampCustomise const&) = delete;\n            void operator=(lampCustomise const&)  = delete;\n\n            std::string filePathOfLang;\n\n            std::vector<std::string> defaultColours = {\n                    \"ffffff-ff\",\n                    \"0a0d12-ff\",\n                    \"141414-ff\",\n                    \"260101-ff\",\n                    \"071216-ff\",\n                    \"590202-ff\",\n                    \"a61103-ff\",\n                    \"260101-ff\",\n                    \"4296f9-4f\",\n                    \"4296f9-cc\",\n                    \"590202-ff\",\n                    \"4296f9-ff\",\n                    \"a61103-ff\",\n                    \"1966bf-c6\", // Colour_SearchHighlight\n                    \"1a994d-ff\" // green-ish color for ButtonAlt\n            };\n\n            float floatMap[15][4] = {\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f },\n                    { 1.0f, 0.0f, 0.0f, 0.0f }, // Colour_SearchHighlight\n                    { 0.0f, 1.0f, 0.0f, 0.0f } // ButtonAlt\n            };\n\n            std::map<ImGuiCol_,std::string> styleMap = {\n                    {ImGuiCol_Text,\"Colour_Text\"},\n                    {ImGuiCol_WindowBg,\"Colour_WindowBackground\"},\n                    {ImGuiCol_PopupBg, \"Colour_ToolTipAndTypes\"},\n                    {ImGuiCol_FrameBg,\"Colour_InputBG\"},\n                    {ImGuiCol_MenuBarBg, \"Colour_MenuBar\"},\n                    {ImGuiCol_Button, \"Colour_Button\"},\n                    {ImGuiCol_ButtonHovered,\"Colour_Hover\"},\n                    {ImGuiCol_ButtonActive,\"Colour_Pressed\"},\n                    {ImGuiCol_Header,\"Colour_MenuItems\"},\n                    {ImGuiCol_HeaderHovered,\"Colour_HeadHover\"},\n                    {ImGuiCol_HeaderActive,\"Colour_HeadPressed\"},\n                    {ImGuiCol_Separator,\"Colour_Separator\"},\n                    {ImGuiCol_SeparatorHovered,\"Colour_SeparatorHover\"}\n            };\n\n            const float defaultFontScale = 1.0f;\n\n\n            void getConfigColours(){\n                int x = 0;\n                for (const auto& pair : styleMap) {\n                    ImGuiCol_ key = pair.first;\n                    const std::string& value = pair.second;\n\n                    std::string loaded = Lamp::Core::FS::lampIO::loadKeyData(value, \"LAMP CONFIG\");\n                    if(loaded == \"\"){\n                        Lamp::Core::Base::lampTypes::lampHexAlpha colour = Lamp::Core::Base::lampTypes::lampHexAlpha(ImGui::GetStyle().Colors[key]);\n                        Lamp::Core::FS::lampIO::saveKeyData(value, ((std::string)colour), \"LAMP CONFIG\");\n                    }else{\n                        ImGui::GetStyle().Colors[key] = Lamp::Core::Base::lampTypes::lampHexAlpha(loaded);\n                    }\n\n\n                    lampCustomise::getInstance().floatMap[x][0] = ImGui::GetStyle().Colors[key].x;\n                    lampCustomise::getInstance().floatMap[x][1] = ImGui::GetStyle().Colors[key].y;\n                    lampCustomise::getInstance().floatMap[x][2] = ImGui::GetStyle().Colors[key].z;\n                    lampCustomise::getInstance().floatMap[x][3] = ImGui::GetStyle().Colors[key].w;\n\n\n                    x++;\n                }\n\n                std::string xloaded = Lamp::Core::FS::lampIO::loadKeyData(\"Colour_SearchHighlight\", \"LAMP CONFIG\");\n                if(xloaded == \"\"){\n                    Lamp::Core::Base::lampTypes::lampHexAlpha colour(lampCustomise::getInstance().defaultColours[x]);\n                    Lamp::Core::FS::lampIO::saveKeyData(\"Colour_SearchHighlight\", ((std::string)colour), \"LAMP CONFIG\");\n                }else{\n                    Lamp::Core::lampControl::getInstance().Colour_SearchHighlight = Lamp::Core::Base::lampTypes::lampHexAlpha(xloaded);\n                }\n\n\n                lampCustomise::getInstance().floatMap[x][0] = ((ImVec4)Lamp::Core::lampControl::getInstance().Colour_SearchHighlight).x;\n                lampCustomise::getInstance().floatMap[x][1] = ((ImVec4)Lamp::Core::lampControl::getInstance().Colour_SearchHighlight).y;\n                lampCustomise::getInstance().floatMap[x][2] = ((ImVec4)Lamp::Core::lampControl::getInstance().Colour_SearchHighlight).z;\n                lampCustomise::getInstance().floatMap[x][3] = ((ImVec4)Lamp::Core::lampControl::getInstance().Colour_SearchHighlight).w;\n\n\n\t\t\t\tx++;\n\t\t\t\tstd::string xloadedBtnAlt = Lamp::Core::FS::lampIO::loadKeyData(\"Colour_ButtonAlt\", \"LAMP CONFIG\");\n                if(xloadedBtnAlt == \"\"){\n                    Lamp::Core::Base::lampTypes::lampHexAlpha colour(lampCustomise::getInstance().defaultColours[x]);\n                    Lamp::Core::FS::lampIO::saveKeyData(\"Colour_ButtonAlt\", ((std::string)colour), \"LAMP CONFIG\");\n                }else{\n                    Lamp::Core::lampControl::getInstance().Colour_ButtonAlt = Lamp::Core::Base::lampTypes::lampHexAlpha(xloadedBtnAlt);\n                }\n\n                lampCustomise::getInstance().floatMap[x][0] = ((ImVec4)Lamp::Core::lampControl::getInstance().Colour_ButtonAlt).x;\n                lampCustomise::getInstance().floatMap[x][1] = ((ImVec4)Lamp::Core::lampControl::getInstance().Colour_ButtonAlt).y;\n                lampCustomise::getInstance().floatMap[x][2] = ((ImVec4)Lamp::Core::lampControl::getInstance().Colour_ButtonAlt).z;\n                lampCustomise::getInstance().floatMap[x][3] = ((ImVec4)Lamp::Core::lampControl::getInstance().Colour_ButtonAlt).w;\n\n                std::string loadedCheckUpdates = Lamp::Core::FS::lampIO::loadKeyData(\"Check_Updates_Startup\", \"LAMP CONFIG\");\n                if(loadedCheckUpdates == \"0\" || loadedCheckUpdates == \"false\"){\n                    lampConfig::getInstance().checkForUpdatesAtStartup = false;\n                }\n            }\n\n            void setColourTemp(ImGuiCol_ StylePoint, Lamp::Core::Base::lampTypes::lampHexAlpha colour){\n                ImGui::GetStyle().Colors[StylePoint] = colour;\n            }\n\n            void setColour(ImGuiCol_ StylePoint, Lamp::Core::Base::lampTypes::lampHexAlpha colour){\n                ImGui::GetStyle().Colors[StylePoint] = colour;\n                Lamp::Core::FS::lampIO::saveKeyData(styleMap[StylePoint], ((std::string)colour), \"LAMP CONFIG\");\n            }\n\n            static bool displayMenu(){\n                ImGuiIO &io = ImGui::GetIO();\n                if (ImGui::CollapsingHeader(lampLang::LS(\"LAMPRAY_CUSTOM_COLOUR\"))) {\n                    int x = 0;\n                    for (const auto &pair: lampCustomise::getInstance().styleMap) {\n                        ImGuiCol_ key = pair.first;\n                        const std::string &value = pair.second;\n                        ImGui::ColorEdit4((pair.second + \"##\" + std::to_string(x)).c_str(),\n                                          lampCustomise::getInstance().floatMap[x]);\n                        lampCustomise::getInstance().setColourTemp(key,\n                                                                   ImVec4(lampCustomise::getInstance().floatMap[x][0],\n                                                                          lampCustomise::getInstance().floatMap[x][1],\n                                                                          lampCustomise::getInstance().floatMap[x][2],\n                                                                          lampCustomise::getInstance().floatMap[x][3]));\n                        x++;\n                    }\n                    ImGui::ColorEdit4((\"Colour_SearchHighlight##\" + std::to_string(x)).c_str(),\n                                      lampCustomise::getInstance().floatMap[x]);\n                    Lamp::Core::lampControl::getInstance().Colour_SearchHighlight = ImVec4(\n                            lampCustomise::getInstance().floatMap[x][0], lampCustomise::getInstance().floatMap[x][1],\n                            lampCustomise::getInstance().floatMap[x][2], lampCustomise::getInstance().floatMap[x][3]);\n\n                    x++;\n                    ImGui::ColorEdit4((\"Colour_ButtonAlt##\" + std::to_string(x)).c_str(),\n                                      lampCustomise::getInstance().floatMap[x]);\n                    Lamp::Core::lampControl::getInstance().Colour_ButtonAlt = ImVec4(\n                            lampCustomise::getInstance().floatMap[x][0], lampCustomise::getInstance().floatMap[x][1],\n                            lampCustomise::getInstance().floatMap[x][2], lampCustomise::getInstance().floatMap[x][3]);\n                    }\n\n                    auto FontScaleHeaderText = lampLang::LS(\"LAMPRAY_CUSTOM_FONT\") + \" ##\"; // make the font scale header id unique from the drag bar to fix the drag behavior\n                    if (ImGui::CollapsingHeader(FontScaleHeaderText)) {\n                        const float MIN_SCALE = 0.3f;\n                        const float MAX_SCALE = 2.0f;\n\n                        ImGui::DragFloat(lampLang::LS(\"LAMPRAY_CUSTOM_FONT\"), &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, \"%.2f\",\n                                         ImGuiSliderFlags_AlwaysClamp);\n                    }\n                    if (ImGui::CollapsingHeader(lampLang::LS(\"LAMPRAY_UPDATE_RULES\"))) {\n                        ImGui::Checkbox(lampLang::LS(\"LAMPRAY_UPDATE_RULES1\")+\" (Check_Updates_Startup)\",\n                                        &lampConfig::getInstance().checkForUpdatesAtStartup);\n                    }\n\n\n                if (ImGui::CollapsingHeader(lampLang::LS(\"LAMPRAY_CUSTOM_LANG\"))) {\n                    std::vector<std::string> xmlFiles;\n                    std::filesystem::path folderPath = \"Lamp_Language\"; // Replace with your folder path\n\n                    if (xmlFiles.empty()) {\n                        try {\n                            for (const auto& entry : std::filesystem::directory_iterator(folderPath)) {\n                                if (entry.is_regular_file() && entry.path().extension() == \".xml\") {\n                                    xmlFiles.push_back(entry.path().string());\n                                }\n                            }\n\n\n                        } catch (const std::exception& e) {\n                        }\n                    }\n\n                    // ImGui dropdown\n                    if (ImGui::BeginCombo(\"##SELECTXML\", lampCustomise::getInstance().filePathOfLang.c_str())) {\n                        for (const auto& xmlFile : xmlFiles) {\n                            bool isSelected = (xmlFile == lampCustomise::getInstance().filePathOfLang);\n                            if (ImGui::Selectable(xmlFile.c_str(), isSelected)) {\n                                lampCustomise::getInstance().filePathOfLang = xmlFile;\n                            }\n                            if (isSelected) {\n                                ImGui::SetItemDefaultFocus();\n                            }\n                        }\n                        ImGui::EndCombo();\n                    }\n                }\n\n\n                if(ImGui::Button(lampLang::LS(\"LAMPRAY_CUSTOM_SV\"))){\n                    ImGui::End();\n\n                    int x = 0;\n                    for (const auto& pair : lampCustomise::getInstance().styleMap) {\n                        ImGuiCol_ key = pair.first;\n                        lampCustomise::getInstance().setColour(key, ImVec4(lampCustomise::getInstance().floatMap[x][0], lampCustomise::getInstance().floatMap[x][1], lampCustomise::getInstance().floatMap[x][2], lampCustomise::getInstance().floatMap[x][3]));\n                        x++;\n                    }\n\n                    Lamp::Core::FS::lampIO::saveKeyData(\"Colour_SearchHighlight\", ((std::string)Lamp::Core::lampControl::getInstance().Colour_SearchHighlight), \"LAMP CONFIG\");\n                    Lamp::Core::FS::lampIO::saveKeyData(\"Colour_ButtonAlt\", ((std::string)Lamp::Core::lampControl::getInstance().Colour_ButtonAlt), \"LAMP CONFIG\");\n\n                    // I don't feel like doing the work to get the value properly, and this seems to work fine\n                    Lamp::Core::FS::lampIO::saveKeyData(\"Font_Scale\", std::to_string(io.FontGlobalScale), \"LAMP CONFIG\");\n                    Lamp::Core::FS::lampIO::saveKeyData(\"Check_Updates_Startup\", std::to_string(lampConfig::getInstance().checkForUpdatesAtStartup), \"LAMP CONFIG\");\n                    Lamp::Core::FS::lampIO::saveKeyData(\"LanguagePath\", lampCustomise::getInstance().filePathOfLang, \"LAMP CONFIG\");\n                    Lamp::Core::lampLang::getInstance().CurrentLanguage = Lamp::Core::lampLang::LanguageContainer();\n                    Lamp::Core::Base::lampLog::getInstance().log(Lamp::Core::lampLang::getInstance().CurrentLanguage.build(lampCustomise::getInstance().filePathOfLang).returnReason);\n                    return true;\n                }\n                ImGui::SameLine();\n                if(ImGui::Button(lampLang::LS(\"LAMPRAY_GOBACK\"))){\n                        ImGui::End();\n                        return true;\n                }\n                ImGui::SameLine();\n                if(ImGui::Button(lampLang::LS(\"LAMPRAY_RESET\"))){\n                    Lamp::Core::lampControl::getInstance().Colour_SearchHighlight = Lamp::Core::Base::lampTypes::lampHexAlpha(lampCustomise::getInstance().defaultColours[13]);\n                    Lamp::Core::lampControl::getInstance().Colour_ButtonAlt = Lamp::Core::Base::lampTypes::lampHexAlpha(lampCustomise::getInstance().defaultColours[14]);\n\n                    for (int i = 0; i < lampCustomise::getInstance().defaultColours.size(); ++i) {\n                        ImVec4 color = Lamp::Core::Base::lampTypes::lampHexAlpha(lampCustomise::getInstance().defaultColours[i]);\n                        lampCustomise::getInstance().floatMap[i][0] = color.x;\n                        lampCustomise::getInstance().floatMap[i][1] = color.y;\n                        lampCustomise::getInstance().floatMap[i][2] = color.z;\n                        lampCustomise::getInstance().floatMap[i][3] = color.w;\n                    }\n\n\n                    io.FontGlobalScale = lampCustomise::getInstance().defaultFontScale;\n                    lampConfig::getInstance().checkForUpdatesAtStartup = lampConfig::getInstance().defaultCheckForUpdateAtStart;\n                }\n\n                ImGui::End();\n                return false;\n            }\n\n        private:\n            lampCustomise(){}\n        };\n    } // Lampray\n} // Core\n\n#endif //LAMP_LAMPCUSTOMISE_H\n"
  },
  {
    "path": "Lampray/Menu/lampMenu.cpp",
    "content": "//\n// Created by charles on 27/09/23.\n#include <cstdlib>\n#include \"lampMenu.h\"\n#include \"lampCustomise.h\"\n#include \"../Lang/lampLang.h\"\n#include \"../Control/lampNotification.h\"\n\nvoid Lamp::Core::lampMenu::RunMenus() {\n\n\n\n    ImGuiIO &io = ImGui::GetIO();\n    ImGui::SetNextWindowSize(io.DisplaySize, 0);\n    ImGui::SetNextWindowPos(ImVec2(0, 0));\n    if(Lamp::Core::lampControl::getInstance().inDeployment){\n        ImGui::Begin(\"DEPLOYMENT\", NULL, Lamp::Core::lampConfig::getInstance().DefaultWindowFlags());\n        ImGui::Text(\"%s\", Lamp::Core::lampControl::getInstance().deploymentStageTitle.c_str());\n        ImGui::Text(\"%s\", (std::to_string(Lamp::Core::lampControl::getInstance().deplopmentTracker.first) + \"/\" + std::to_string(Lamp::Core::lampControl::getInstance().deplopmentTracker.second)).c_str());\n        ImGui::End();\n    }else {\n        switch (currentMenu) {\n            case LICENCE_MENU:\n                if (Lamp::Core::lampConfig::getInstance().lampFlags[\"showIntroMenu\"].as_bool()) {\n                    IntroMenu();\n                } else {\n                    currentMenu = GAME_MOD_MENU;\n                }\n                break;\n            case GAME_MOD_MENU:\n                ModMenu();\n                break;\n            case GAME_CONFIG_MENU:\n                GameConfigMenu();\n                break;\n            case CUSTOMIZE:\n                ImGuiWindowFlags windowFlags = 0;\n                windowFlags += ImGuiWindowFlags_NoMove;\n                windowFlags += ImGuiWindowFlags_NoResize;\n                windowFlags += ImGuiWindowFlags_NoCollapse;\n                windowFlags += ImGuiWindowFlags_NoTitleBar;\n                windowFlags += ImGuiWindowFlags_MenuBar;\n                ImGui::Begin(\"Colour Menu\",NULL,windowFlags);\n                DefaultMenuBar();\n                if(Lamp::Core::lampCustomise::displayMenu()){\n                    currentMenu = GAME_MOD_MENU;\n                }\n                break;\n        }\n        //currentMenu = LICENCE_MENU;\n        createProfileDialog();\n    }\n}\n\nvoid Lamp::Core::lampMenu::IntroMenu() {\n\n    ImGui::Begin(\"Requires a title, but we've disabled it.\", NULL, Lamp::Core::lampConfig::getInstance().DefaultWindowFlags());\n    DefaultMenuBar();\n    ImGui::Separator();\n    ImGuiStyle& style = ImGui::GetStyle();\n    float size = ImGui::CalcTextSize(\"Lampray\").x + style.FramePadding.x * 2.0f;\n    float avail = ImGui::GetContentRegionAvail().x;\n\n    float off = (avail - size) * 0.5f;\n    if (off > 0.0f){ImGui::SetCursorPosX(ImGui::GetCursorPosX() + off);}\n    ImGui::Text(\"Lampray\");\n\n    size = ImGui::CalcTextSize(lampLang::LS(\"LAMPRAY_LONGNAME\")).x + style.FramePadding.x * 2.0f;\n    off = (avail - size) * 0.5f;\n    if (off > 0.0f){ImGui::SetCursorPosX(ImGui::GetCursorPosX() + off);}\n    ImGui::Text(\"%s\", lampLang::LS(\"LAMPRAY_LONGNAME\").c_str());\n    ImGui::Separator();\n    ImGui::Text(\"%s\", lampLang::LS(\"LAMPRAY_LICENSE\").c_str());\n\n\n\n    if(ImGui::Button(lampLang::LS(\"LAMPRAY_AKNOWL\"))){\n        Lamp::Core::lampConfig::getInstance().lampFlags[\"showIntroMenu\"] = \"0\";\n        Lamp::Core::FS::lampIO::saveKeyData(\"showIntroMenu\", Lamp::Core::lampConfig::getInstance().lampFlags[\"showIntroMenu\"], \"LAMP CONFIG\");\n        currentMenu = GAME_MOD_MENU;\n    }\n    ImGui::End();\n}\n\n\nvoid Lamp::Core::lampMenu::ModMenu() {\n\n    ImGui::Begin(\"Blank Menu\", NULL, Lamp::Core::lampConfig::getInstance().DefaultWindowFlags());\n    DefaultMenuBar();\n\n    Lamp::Core::lampNotification::getInstance().DisplayNotifications();\n\n    ImGuiStyle& style = ImGui::GetStyle();\n    float size = ImGui::CalcTextSize(lampLang::LS(\"LAMPRAY_DRAGANDDROP\")).x + style.FramePadding.x * 2.0f;\n    float avail = ImGui::GetContentRegionAvail().x;\n\n    float off = (avail - size) * 0.5f;\n    if (off > 0.0f){ImGui::SetCursorPosX(ImGui::GetCursorPosX() + off);}\n    ImGui::Text(\"%s\", lampLang::LS(\"LAMPRAY_DRAGANDDROP\").c_str());\n\n    size = ImGui::CalcTextSize(lampLang::LS(\"LAMPRAY_DEPLOY\")).x + style.FramePadding.x * 2.0f;\n    avail = ImGui::GetContentRegionAvail().x;\n\n    off = (avail - size) * 0.5f;\n    if (off > 0.0f){ImGui::SetCursorPosX(ImGui::GetCursorPosX() + off);}\n    if(ImGui::Button(lampLang::LS(\"LAMPRAY_DEPLOY\"))) {\n        deployCheck = true;\n    }\n\n    size = ImGui::CalcTextSize(lampLang::LS(\"LAMPRAY_RESET\")).x + style.FramePadding.x * 2.0f;\n    avail = ImGui::GetContentRegionAvail().x;\n\n    off = (avail - size) * 0.5f;\n    if (off > 0.0f){ImGui::SetCursorPosX(ImGui::GetCursorPosX() + off);}\n    if(ImGui::Button(lampLang::LS(\"LAMPRAY_RESET\"))) {\n        Lamp::Core::FS::lampTrack::reset(Lamp::Games::getInstance().currentGame->Ident().ReadableName);\n        std::filesystem::path installPath(Lamp::Games::getInstance().currentGame->KeyInfo()[\"installDirPath\"]);\n        system((\"pkexec umount \\\"\"+Lamp::Games::getInstance().currentGame->KeyInfo()[\"installDirPath\"]+\"\\\"\").c_str());\n        std::filesystem::rename(installPath.parent_path() / (\"Lampray Managed - \" + installPath.stem().string()), Lamp::Games::getInstance().currentGame->KeyInfo()[\"installDirPath\"]);\n        system((\"pkexec umount \\\"\"+Lamp::Games::getInstance().currentGame->KeyInfo()[\"appDataPath\"]+\"/Mods\\\"\").c_str());\n        std::filesystem::rename(std::filesystem::path(Lamp::Games::getInstance().currentGame->KeyInfo()[\"appDataPath\"]+\"/Mods\").parent_path() / (\"Lampray Managed - \" + std::filesystem::path(Lamp::Games::getInstance().currentGame->KeyInfo()[\"appDataPath\"]+\"/Mods\").stem().string()), std::filesystem::path(Lamp::Games::getInstance().currentGame->KeyInfo()[\"appDataPath\"]+\"/Mods\"));\n    }\n\n\n    if(deployCheck) {\n        if(!Lamp::Games::getInstance().currentGame->installPathSet()){\n            Lamp::Core::lampNotification::getInstance().pushNotification(\"warning\", Lamp::Games::getInstance().currentGame->Ident().ReadableName + Lamp::Core::lampLang::getInstance().LS(\"LAMPRAY_WARN_GAME_PATH\"), true);\n        }\n\n        ImGuiIO &io = ImGui::GetIO();\n        ImGui::SetNextWindowSize(io.DisplaySize, 0);\n        ImGui::SetNextWindowPos(ImVec2(0, 0));\n\n        ImGui::Begin(lampLang::LS(\"LAMPRAY_CHECK\"), NULL, Lamp::Core::lampConfig::getInstance().DefaultWindowFlags());\n        Lamp::Core::lampNotification::getInstance().DisplayNotifications();\n        ImGui::Text(\"%s\", lampLang::LS(\"LAMPRAY_STARTTEXT\").c_str());\n\n        if(ImGui::Button(lampLang::LS(\"LAMPRAY_START\"))){\n            deployCheck = !deployCheck;\n\n            std::thread([] {\n                Lamp::Core::Base::LampSequencer::add(\"Deployment Start\",[]() -> lampReturn {\n                    Lamp::Core::FS::lampTrack::reset(Lamp::Games::getInstance().currentGame->Ident().ReadableName);\n\n                    return Lamp::Core::Base::lampLog::getInstance().pLog({1, \"Task Complete.\"});\n                });\n                Lamp::Core::Base::LampSequencer::add(\"Deployment Start\",[]() -> lampReturn {\n                    Lamp::Games::getInstance().currentGame->startDeployment();\n                    return Lamp::Core::Base::lampLog::getInstance().pLog({1, \"Task Complete.\"});\n                });\n\n                Lamp::Core::Base::LampSequencer::run(\"Deployment Start\");\n            }).detach();\n\n        }\n        ImGui::SameLine();\n        if(ImGui::Button(lampLang::LS(\"LAMPRAY_GOBACK\"))){ deployCheck = !deployCheck; }\n\n        ImGui::End();\n    }\n\n    ImGui::Separator();\n    Lamp::Games::getInstance().currentGame->listArchives();\n    ImGui::End();\n}\n\nvoid Lamp::Core::lampMenu::GameConfigMenu() {\n\n    ImGuiWindowFlags windowFlags = 0;\n    windowFlags += ImGuiWindowFlags_NoMove;\n    windowFlags += ImGuiWindowFlags_NoResize;\n    windowFlags += ImGuiWindowFlags_NoCollapse;\n    windowFlags += ImGuiWindowFlags_NoTitleBar;\n    windowFlags += ImGuiWindowFlags_MenuBar;\n    ImGui::Begin(\"testConfig\",NULL,windowFlags);\n    Lamp::Games::getInstance().currentGame->ConfigMenu();\n    ImGui::Separator();\n    if(ImGui::Button(lampLang::LS(\"LAMPRAY_GOBACK\"))){\n        currentMenu = GAME_MOD_MENU;\n    }\n\n\n    ImGui::End();\n}\n\nvoid Lamp::Core::lampMenu::DefaultMenuBar() {\n    if (ImGui::BeginMenuBar()) {\n\n        if(ImGui::BeginMenu(\"Lampray - \" + Lamp::Games::getInstance().currentGame->Ident().ReadableName +\" - \"+Lamp::Games::getInstance().currentProfile)){\n\n            if (ImGui::BeginMenu(lampLang::LS(\"LAMPRAY_GAMEPICK\"))) {\n                int gameCount = 0;\n                for (Game::gameControl *element: Lamp::Games::getInstance().gameList){\n                    if(ImGui::MenuItem(element->Ident().ReadableName)){\n                        Lamp::Games::getInstance().currentGame = element;\n                        Lamp::Games::getInstance().currentGameInt = gameCount;\n                        Lamp::Core::FS::lampIO::saveKeyData(\"PreviousGame\",std::to_string(gameCount), \"LAMP CONFIG\");\n                    }\n                    gameCount++;\n                }\n                ImGui::EndMenu();\n            }\n\n            ImGui::MenuItem(\"--------\");\n            if(currentMenu == GAME_MOD_MENU){\n            if (ImGui::MenuItem(lampLang::LS(\"LAMPRAY_GAMECONFIG\"))) {\n                currentMenu = GAME_CONFIG_MENU;\n            }\n\n            if(ImGui::BeginMenu(lampLang::LS(\"LAMPRAY_PROFSELECT\"))){\n\n                for (std::string x: Lamp::Core::Base::lampMod::Profile::splitString(Lamp::Games::getInstance().currentGame->KeyInfo()[\"ProfileList\"])) {\n\n                    if (x == Lamp::Games::getInstance().currentProfile) {\n                        if (ImGui::BeginMenu((\"* \"+x+\" *\").c_str())) {\n                            if (ImGui::MenuItem(lampLang::LS(\"LAMPRAY_PROFLOAD\"))) {\n                                Lamp::Games::getInstance().currentProfile = x;\n                                Lamp::Games::getInstance().currentGame->getModList().clear();\n                                Lamp::Games::getInstance().currentGame->getModList() = FS::lampIO::loadModList(\n                                        Lamp::Games::getInstance().currentGame->Ident().ShortHand, x);\n                                FS::lampIO::saveKeyData(\"CurrentProfile\", x, Lamp::Games::getInstance().currentGame->Ident().ShortHand);\n                            }\n                            if (x != \"Default\") {\n                                if (ImGui::MenuItem(lampLang::LS(\"LAMPRAY_PROFDEL\"))) {\n\n                                }\n                            }\n                            ImGui::EndMenu();\n                        }\n                    }else{\n                        if (ImGui::BeginMenu(x.c_str())) {\n                            if (ImGui::MenuItem(lampLang::LS(\"LAMPRAY_PROFLOAD\"))) {\n                                Lamp::Games::getInstance().currentProfile = x;\n                                Lamp::Games::getInstance().currentGame->getModList().clear();\n                                Lamp::Games::getInstance().currentGame->getModList() = FS::lampIO::loadModList(\n                                        Lamp::Games::getInstance().currentGame->Ident().ShortHand, x);\n                                FS::lampIO::saveKeyData(\"CurrentProfile\", x, Lamp::Games::getInstance().currentGame->Ident().ShortHand);\n                            }\n                            if (x != \"Default\") {\n                                if (ImGui::MenuItem(lampLang::LS(\"LAMPRAY_PROFDEL\"))) {\n                                    Lamp::Core::Base::lampMod::Profile::removeValue(Lamp::Games::getInstance().currentGame->KeyInfo()[\"ProfileList\"],x);\n                                    FS::lampIO::saveKeyData(\"ProfileList\", Lamp::Games::getInstance().currentGame->KeyInfo()[\"ProfileList\"], Lamp::Games::getInstance().currentGame->Ident().ShortHand);\n                                }\n                            }\n                            ImGui::EndMenu();\n                        }\n                    }\n\n\n\n                }\n                ImGui::MenuItem(\"--------\");\n\n\n                if(ImGui::MenuItem(lampLang::LS(\"LAMPRAY_PROFCRE\"))){\n                    displayProfileCreateMenu = true;\n                }\n                ImGui::EndMenu();\n            }\n\n\n\n            ImGui::MenuItem(\"--------\");\n\n            if (ImGui::BeginMenu(lampLang::LS(\"LAMPRAY_PROFEXP\"))) {\n                for (std::string x: Lamp::Core::Base::lampMod::Profile::splitString(\n                        Lamp::Games::getInstance().currentGame->KeyInfo()[\"ProfileList\"])) {\n                        if (ImGui::MenuItem((x).c_str())) {\n                            Lamp::Core::FS::lampShare::exportProfile(x);\n                        }\n                }\n                ImGui::EndMenu();\n            }\n\n\n            if (ImGui::MenuItem(lampLang::LS(\"LAMPRAY_PROFIMP\"))) {\n                Lamp::Core::FS::lampShare::importProfile();\n            }\n\n\n\n            ImGui::MenuItem(\"--------\");\n            }\n\n\n\n            if (ImGui::MenuItem(lampLang::LS(\"LAMPRAY_UPDATE_CHECK\"))) {\n                Lamp::Core::FS::lampUpdate::getInstance().checkForUpdates();\n            }\n\n            if (ImGui::MenuItem(lampLang::LS(\"LAMPRAY_CUSTOM\"))) {\n                currentMenu = CUSTOMIZE;\n            }\n\n\n            if (ImGui::BeginMenu(lampLang::LS(\"LAMPRAY_ABT\"))) {\n                ImGui::MenuItem(\"Lampray (Linux Application Modding Platform)\");\n                ImGui::MenuItem(\"--------------------------------------\");\n                ImGui::MenuItem(lampLang::LS(\"LAMPRAY_ABT1\")+\" Charles Hollingworth\");\n                ImGui::MenuItem(\"-------------Contributors-------------\");\n                ImGui::MenuItem(\"Jinxtaposition\");\n                ImGui::MenuItem(\"Airtonix\");\n                ImGui::MenuItem(\"SnazzyPanda\");\n                ImGui::MenuItem(\"makiftasova\");\n                ImGui::MenuItem(\"internetisaiah\");\n                ImGui::MenuItem(\"MagicD3VIL\");\n                ImGui::MenuItem(\"Invisible Friend\");\n                ImGui::MenuItem(\"-------------Donators-------------\");\n                ImGui::MenuItem(\"Plarpoon\");\n                ImGui::MenuItem(\"Den\");\n                ImGui::EndMenu();\n            }\n            ImGui::MenuItem(\"--------\");\n            if (ImGui::MenuItem(lampLang::LS(\"LAMPRAY_QUIT\"))) {\n                this->userRequestedQuit = true;\n            }\n\n\n            ImGui::EndMenu();\n        }\n\n\n        Lamp::Core::FS::lampUpdate::getInstance().getExpression();\n        ImGui::EndMenuBar();\n}\n}\n\nvoid Lamp::Core::lampMenu::createProfileDialog() {\n        if(!displayProfileCreateMenu) return;\n        ImGui::Begin(lampLang::LS(\"LAMPRAY_PROFCRE\"));\n\n        if(ImGui::BeginMenu(lampLang::LS(\"LAMPRAY_PROFFRM\"))){\n\n            for (std::string x: Lamp::Core::Base::lampMod::Profile::splitString(Lamp::Games::getInstance().currentGame->KeyInfo()[\"ProfileList\"])) {\n                if(pfCopyStr == x){\n                    if (ImGui::MenuItem((\"*\" + x + \"*\").c_str())) {\n                        pfCopyStr = x;\n                        pfCopy = true;\n                    }\n                }else {\n                    if (ImGui::MenuItem(x.c_str())) {\n                        pfCopyStr = x;\n                        pfCopy = true;\n                    }\n                }\n            }\n            ImGui::MenuItem(\"--------\");\n            if(ImGui::MenuItem(lampLang::LS(\"LAMPRAY_PROFNON\"))){\n                pfCopyStr = \"\";\n                pfCopy = false;\n            }\n            ImGui::EndMenu();\n        }\n\n\n        ImGui::Text(\"%s\", lampLang::LS(\"LAMPRAY_PROFNM\").c_str());\n        if(ImGui::InputText(\"##inputProf\",profileBuffer,250, ImGuiInputTextFlags_EnterReturnsTrue) || ImGui::Button(lampLang::LS(\"LAMPRAY_PROFCRES\"))){\n            std::string pb(profileBuffer);\n            Lamp::Core::Base::lampMod::Profile::addValue(Lamp::Games::getInstance().currentGame->KeyInfo()[\"ProfileList\"],pb);\n\n            if(pfCopyStr != \"\" && pfCopy){\n                if(Lamp::Games::getInstance().currentProfile != pfCopyStr) {\n                    Lamp::Games::getInstance().currentGame->getModList() = FS::lampIO::loadModList(\n                            Lamp::Games::getInstance().currentGame->Ident().ShortHand, pfCopyStr);\n                }\n                FS::lampIO::saveModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand,Lamp::Games::getInstance().currentGame->getModList(),pb);\n            }\n            Lamp::Games::getInstance().currentGame->getModList() = FS::lampIO::loadModList(Lamp::Games::getInstance().currentGame->Ident().ShortHand,pb);\n            Lamp::Games::getInstance().currentProfile = pb;\n            FS::lampIO::saveKeyData(\"ProfileList\", Lamp::Games::getInstance().currentGame->KeyInfo()[\"ProfileList\"], Lamp::Games::getInstance().currentGame->Ident().ShortHand);\n            memset(profileBuffer, 0, sizeof(profileBuffer));\n            pfCopyStr = \"\";\n            pfCopy = false;\n            displayProfileCreateMenu = false;\n        }\n        ImGui::SameLine();\n    if(ImGui::Button(lampLang::LS(\"LAMPRAY_GOBACK\"))){\n        displayProfileCreateMenu = false;\n    }\n        ImGui::End();\n}\n"
  },
  {
    "path": "Lampray/Menu/lampMenu.h",
    "content": "//\n// Created by charles on 27/09/23.\n//\n\n#ifndef LAMP_LAMPMENU_H\n#define LAMP_LAMPMENU_H\n\n#include \"../../third-party/imgui/imgui.h\"\n#include \"../Filesystem/lampFS.h\"\n#include \"../Control/lampGames.h\"\n#include \"../Control/lampControl.h\"\nnamespace Lamp::Core{\n    class lampMenu{\n    public:\n        enum Menus{\n            LICENCE_MENU,\n            GAME_MOD_MENU,\n            GAME_CONFIG_MENU,\n            CUSTOMIZE\n        };\n\n        Menus currentMenu = LICENCE_MENU;\n        bool userRequestedQuit = false;\n\n        void RunMenus();\n\n    private:\n\n        char profileBuffer[250]{};\n\n        bool deployCheck = false;\n\n        void IntroMenu();\n        void ModMenu();\n        void GameConfigMenu();\n        std::string pfCopyStr;\n        bool pfCopy;\n        bool displayProfileCreateMenu;\n        void createProfileDialog();\n        void DefaultMenuBar();\n    };\n}\n#endif //LAMP_LAMPMENU_H\n"
  },
  {
    "path": "Lampray/Parse/lampParse.h",
    "content": "//\n// Created by charles on 27/09/23.\n//\n\n#ifndef LAMP_LAMPPARSE_H\n#define LAMP_LAMPPARSE_H\n\n#include <string>\n#include <regex>\n#include \"../Control/lampConfig.h\"\n#include \"../../third-party/json/json.hpp\"\n#include \"../../third-party/l4z/lz4frame.h\"\n#include <lz4.h>\n\nnamespace Lamp::Core::Parse{\n    class lampFoModParser{\n\n    };\n\n\n    // Go Star smacx250s project for this amazing pearl script made c++ https://github.com/smacx250/BG3\n    class lampBG3PakParser{\n    private:\n\n        static constexpr int FILE_HDR_LEN = 24;\n        static constexpr int TBL_HDR_LEN = 8;\n        static constexpr int TBL_ENT_LEN = 272;\n\n        static std::vector<char> uncompressTableEntries(const std::vector<char>& compressedTableEntries) {\n            size_t maxDecompressedSize = TBL_ENT_LEN * compressedTableEntries.size(); // Max possible size\n            std::vector<char> tblEntries(maxDecompressedSize);\n\n            int decompressedSize = LZ4_decompress_safe(compressedTableEntries.data(), tblEntries.data(), compressedTableEntries.size(), maxDecompressedSize);\n\n            if (decompressedSize < 0) {\n                std::cerr << \"Error during LZ4 decompression\" << std::endl;\n                exit(1);\n            }\n\n            // Resize the vector to the actual decompressed size\n            tblEntries.resize(decompressedSize);\n            return tblEntries;\n        }\n\n        static bool extractMetadataFromLSX(const std::string& lsxData) {\n            bool gotModInfo = false;\n            std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath + \"/Baldur's Gate 3\";\n            std::string UUID;\n            std::string Folder;\n            std::string Version;\n            std::string MD5;\n            std::string Name;\n\n            std::regex pattern(\"value=\\\"([^\\\"]+)\\\"\");\n\n            size_t pos = 0;\n            while ((pos = lsxData.find('<', pos)) != std::string::npos) {\n                size_t endPos = lsxData.find('>', pos);\n                if (endPos != std::string::npos) {\n                    std::string attribute = lsxData.substr(pos + 1, endPos - pos - 1);\n\n                    if (attribute.find(\"ModuleInfo\") != std::string::npos) {\n                        gotModInfo = true;\n                    }\n\n                    if (gotModInfo){\n\n                        std::smatch match;\n                        std::string value;\n                        if (std::regex_search(attribute, match, pattern)) {\n                            if (match.size() > 1) {\n                                value = match[1];\n                            }\n                        }\n\n                        if(attribute.find(\"Folder\") != std::string::npos){\n                            Folder = value;\n                        }\n                        if(attribute.find(\"MD5\") != std::string::npos){\n                            MD5 = value;\n                        }\n                        if(attribute.find(\"Name\") != std::string::npos){\n                            Name = value;\n                        }\n                        if(attribute.find(\"UUID\") != std::string::npos){\n                            UUID = value;\n                        }\n                        if(attribute.find(\"Version\") != std::string::npos){\n                            Version = value;\n                        }\n                    }\n\n\n                    pos = endPos + 1;\n                }\n            }\n            pugi::xml_document doc;\n            while (!doc.load_file((workingDir + \"/PlayerProfiles/Public/modsettings.lsx\").c_str())) {\n                std::cerr << \"Failed to load XML file.\" << std::endl;\n                return false;\n            }\n\n            pugi::xml_node moduleNode = doc.select_node(\"//node[@id='Mods']\").node();\n            pugi::xml_node childrenNode = moduleNode.child(\"children\");\n\n            pugi::xml_node newShortDescNode = childrenNode.append_child(\"node\");\n            newShortDescNode.append_attribute(\"id\") = \"ModuleShortDesc\";\n\n            pugi::xml_node folderAttrib = newShortDescNode.append_child(\"attribute\");\n            folderAttrib.append_attribute(\"id\") = \"Folder\";\n            folderAttrib.append_attribute(\"type\") = \"LSString\";\n            folderAttrib.append_attribute(\"value\") = Folder.c_str();\n\n            pugi::xml_node md5Attrib = newShortDescNode.append_child(\"attribute\");\n            md5Attrib.append_attribute(\"id\") = \"MD5\";\n            md5Attrib.append_attribute(\"type\") = \"LSString\";\n            md5Attrib.append_attribute(\"value\") = MD5.c_str();\n\n            pugi::xml_node nameAttrib = newShortDescNode.append_child(\"attribute\");\n            nameAttrib.append_attribute(\"id\") = \"Name\";\n            nameAttrib.append_attribute(\"type\") = \"LSString\";\n            nameAttrib.append_attribute(\"value\") = Name.c_str();\n\n            pugi::xml_node uuidAttrib = newShortDescNode.append_child(\"attribute\");\n            uuidAttrib.append_attribute(\"id\") = \"UUID\";\n            uuidAttrib.append_attribute(\"type\") = \"guid\";\n            uuidAttrib.append_attribute(\"value\") = UUID.c_str();\n\n\n            pugi::xml_node versionAttrib = newShortDescNode.append_child(\"attribute\");\n            versionAttrib.append_attribute(\"id\") = \"Version64\";\n            versionAttrib.append_attribute(\"value\") = Version.c_str();\n            versionAttrib.append_attribute(\"type\") = \"int64\";\n\n\n            if (!doc.save_file((workingDir + \"/PlayerProfiles/Public/modsettings.lsx\").c_str())) {\n                std::cerr << \"Failed to save XML file.\" << std::endl;\n                return false;\n            }\n\n\n            return true;\n        }\n\n    public:\n\n        static bool extractJsonData(std::string ArchivePath) {\n           std::vector<int> SUP_VER = {15, 18};\n           std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath + \"/Baldur's Gate 3\";\n           int countedSucsesses = 0;\n           for (const auto& entry : std::filesystem::directory_iterator((workingDir + \"/ext/\" + std::filesystem::path(ArchivePath).filename().stem().string()).c_str())) {\n               if(std::regex_match (entry.path().filename().string(), std::regex(\"^.*\\\\.(pak)$\") )) {\n                   // debugging from pearl script. set to 5 for full info dump;\n                   int argc = 0;\n\n                   const std::string pakName = entry.path().string();\n\n                   // Open the file\n                   std::ifstream pakFile(pakName, std::ios::binary);\n                   if (!pakFile.is_open()) {\n                       std::cerr << \"Can't open '\" << pakName << \"' for reading\" << std::endl;\n                       return 0;\n                   }\n\n                   // Read the file header\n                   std::vector<char> fileHeader(FILE_HDR_LEN);\n                   if (!pakFile.read(fileHeader.data(), FILE_HDR_LEN)) {\n                       std::cerr << \"Unable to read file header from '\" << pakName << \"'\" << std::endl;\n                       return 0;\n                   }\n\n                   // Check ID\n                   if (std::string(fileHeader.data(), 4) != \"LSPK\") {\n                       std::cerr << \"Invalid file ID, need 'LSPK', got '\" << std::string(fileHeader.data(), 4) << \"'\" << std::endl;\n                       return 0;\n                   }\n\n                   // Check version\n                   uint32_t version = *reinterpret_cast<uint32_t*>(&fileHeader[4]);\n                   if (version < SUP_VER[0] || version > SUP_VER[1]) {\n                       std::cerr << \"Warning: file version '\" << version << \"' is outside of values believed to work [\"\n                                 << SUP_VER[0] << \",\" << SUP_VER[1] << \"]!\" << std::endl;\n                   }\n\n                   // Get table offset\n                   uint64_t tblOff = *reinterpret_cast<uint64_t*>(&fileHeader[8]);\n\n                   // Read the table header\n                   pakFile.seekg(tblOff);\n                   std::vector<char> tblHeader(TBL_HDR_LEN);\n                   if (!pakFile.read(tblHeader.data(), TBL_HDR_LEN)) {\n                       std::cerr << \"Unable to read table header from '\" << pakName << \"'\" << std::endl;\n                       return 0;\n                   }\n\n                   uint32_t numFiles = *reinterpret_cast<uint32_t*>(&tblHeader[0]);\n                   uint32_t tblCmpLen = *reinterpret_cast<uint32_t*>(&tblHeader[4]);\n                   uint32_t tblLen = numFiles * TBL_ENT_LEN;\n\n                   // Read the compressed table entries\n                   std::vector<char> cmpTblEntries(tblCmpLen);\n                   if (!pakFile.read(cmpTblEntries.data(), tblCmpLen)) {\n                       std::cerr << \"Unable to read table entries from '\" << pakName << \"'\" << std::endl;\n                       return 0;\n                   }\n\n                   if (argc > 2) {\n                       std::ofstream ctblOut(\"ctbl.bin\", std::ios::binary);\n                       ctblOut.write(cmpTblEntries.data(), cmpTblEntries.size());\n                   }\n\n                   // Uncompress the table entries\n                   std::vector<char> tblEntries = uncompressTableEntries(cmpTblEntries);\n\n                   if (argc > 2) {\n                       std::ofstream uctblOut(\"uctbl.bin\", std::ios::binary);\n                       uctblOut.write(tblEntries.data(), tblEntries.size());\n                   }\n\n                   // Iterate through the files, looking for \"meta.lsx\"\n                   uint64_t fOfst = 0;\n                   uint32_t fcLen = 0;\n                   uint32_t fLen = 0;\n                   for (int i = 0; i < numFiles; i++) {\n                       int entOff = i * TBL_ENT_LEN;\n                       std::string fName(tblEntries.data() + entOff, 256);\n\n                       if (fName.find(\"meta.lsx\") != std::string::npos) {\n                           entOff += 256;\n                           fOfst = *reinterpret_cast<uint64_t*>(tblEntries.data() + entOff) & 0x00ffffffffffffff;\n                           fcLen = *reinterpret_cast<uint32_t*>(tblEntries.data() + entOff + 8);\n                           fLen = *reinterpret_cast<uint32_t*>(tblEntries.data() + entOff + 12);\n\n                           break;\n                       }\n                   }\n\n                   // Couldn't find it!\n                   if (fOfst == 0) {\n                       std::cerr << \"Was not able to find 'meta.lsx' in '\" << pakName << \"'\" << std::endl;\n                       return 0;\n                   }\n\n                   // Now read the file\n                   pakFile.seekg(fOfst);\n                   std::vector<char> rawFile(fcLen);\n                   if (!pakFile.read(rawFile.data(), fcLen)) {\n                       std::cerr << \"Unable to read 'meta.lsx' file from '\" << pakName << \"'\" << std::endl;\n                       return 0;\n                   }\n\n                   std::vector<char> ucFile;\n                   if (fLen == 0) {\n                       ucFile = rawFile;\n                   } else {\n                       ucFile = uncompressTableEntries(rawFile);\n                   }\n\n                   if (argc > 2) {\n                       std::cout << \"\\nmeta.lsx:\\n\" << std::string(ucFile.data(), ucFile.size()) << \"\\n\";\n                   }\n\n                   std::vector<std::string> seenAttr;\n\n                   // This is a poor fix, however will fix the incosistancy with data extraction.\n                   countedSucsesses += extractMetadataFromLSX(std::string(ucFile.data(), ucFile.size()));\n\n\n\n               }}\n           return true;\n        }\n        static bool collectJsonData(std::string ArchivePath){\n            std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath + \"/Baldur's Gate 3\";\n\n            for (const auto& entry : std::filesystem::directory_iterator((workingDir + \"/ext/\" + std::filesystem::path(ArchivePath).filename().stem().string()).c_str())) {\n                if(std::regex_match (entry.path().filename().string(), std::regex(\"^.*\\\\.(json)$\") )) {\n\n                    std::ifstream jsonFile(entry.path());\n                    pugi::xml_document doc;\n\n                    if (!jsonFile.is_open()) {\n                        std::cerr << \"Failed to open JSON file.\" << std::endl;\n                        return false;\n                    }\n\n\n                    while (!doc.load_file((workingDir + \"/PlayerProfiles/Public/modsettings.lsx\").c_str())) {\n                        std::cerr << \"Failed to load XML file.\" << std::endl;\n                        // return false;\n                    }\n\n                    nlohmann::json jsonData;\n                    jsonFile >> jsonData;\n                    jsonFile.close();\n\n                    if (jsonData.contains(\"mods\") || jsonData.contains(\"Mods\")) {\n\n                        std::string packString = \"mods\";\n                        if(jsonData.contains(\"Mods\")){\n                            packString = \"Mods\";\n                        }\n\n                        for (const auto& mod : jsonData[packString]) {\n                            std::string modName;\n                            if(mod.contains(\"modName\")){\n                                modName = mod[\"modName\"];\n                            }else if(mod.contains(\"ModName\")){\n                                modName = mod[\"ModName\"];\n                            }else{\n                                modName = mod[\"Name\"];\n                            }\n\n                            std::string folderName;\n                            if(mod.contains(\"folderName\")){\n                                folderName = mod[\"folderName\"];\n                            }else if(mod.contains(\"FolderName\")){\n                                folderName = mod[\"FolderName\"];\n                            }else{\n                                folderName = mod[\"Folder\"];\n                            }\n\n                            std::string UUID = mod[\"UUID\"];\n\n                            pugi::xml_node moduleNode = doc.select_node(\"//node[@id='Mods']\").node();\n                            pugi::xml_node childrenNode = moduleNode.child(\"children\");\n\n                            pugi::xml_node newShortDescNode = childrenNode.append_child(\"node\");\n                            newShortDescNode.append_attribute(\"id\") = \"ModuleShortDesc\";\n\n                            pugi::xml_node folderAttrib = newShortDescNode.append_child(\"attribute\");\n                            folderAttrib.append_attribute(\"id\") = \"Folder\";\n                            folderAttrib.append_attribute(\"type\") = \"LSString\";\n                            folderAttrib.append_attribute(\"value\") = folderName.c_str();\n\n                            pugi::xml_node md5Attrib = newShortDescNode.append_child(\"attribute\");\n                            md5Attrib.append_attribute(\"id\") = \"MD5\";\n                            md5Attrib.append_attribute(\"type\") = \"LSString\";\n                            md5Attrib.append_attribute(\"value\") = \"\";\n\n                            pugi::xml_node nameAttrib = newShortDescNode.append_child(\"attribute\");\n                            nameAttrib.append_attribute(\"id\") = \"Name\";\n                            nameAttrib.append_attribute(\"type\") = \"LSString\";\n                            nameAttrib.append_attribute(\"value\") = modName.c_str();\n\n                            pugi::xml_node uuidAttrib = newShortDescNode.append_child(\"attribute\");\n                            uuidAttrib.append_attribute(\"id\") = \"UUID\";\n                            uuidAttrib.append_attribute(\"type\") = \"guid\";\n                            uuidAttrib.append_attribute(\"value\") = UUID.c_str();\n\n\n                            pugi::xml_node versionAttrib = newShortDescNode.append_child(\"attribute\");\n                            versionAttrib.append_attribute(\"id\") = \"Version64\";\n                            versionAttrib.append_attribute(\"value\") = \"36028797018963968\";\n                            versionAttrib.append_attribute(\"type\") = \"int64\";\n\n\n                            if (!doc.save_file((workingDir + \"/PlayerProfiles/Public/modsettings.lsx\").c_str())) {\n                                std::cerr << \"Failed to save XML file.\" << std::endl;\n                            }\n\n                            return true;\n                        }\n                    } else {\n                        std::cerr << \"JSON data does not contain 'mods' key.\" << std::endl;\n                        return false;\n                    }\n\n                }\n            }\n\n            return false;\n        }\n\n    };\n}\n#endif //LAMP_LAMPPARSE_H\n"
  },
  {
    "path": "README.md",
    "content": "# ![Lampray](logo/LMP-64.png) Lampray\n\nLampray is a mod manager for gaming on Linux! If you'd like to help improve Lampray, you can:\n\n- [Report an issue](https://github.com/CHollingworth/Lampray/issues/new?assignees=&labels=bug&projects=&template=-game--bug-report.md&title=)\n- [Request a feature](https://github.com/CHollingworth/Lampray/issues/new?assignees=&labels=enhancement&projects=&template=feature_request.md&title=)\n- [Join the Discord](https://discord.gg/5macMedevy)\n- [Contribute](./CONTRIBUTING.md)\n\nIf you'd like to learn more about Lampray, see [Lampray Docs](./docs/lampray-docs.md).\n\n## Dependencies\n\nLampray requires the following:\n\n| Type        | Name                 |\n|-------------|----------------------|\n| Build Tool  | C++                  |\n| Build Tool  | CMake                |\n| Build Tool  | GCC                  |\n| Build Tool  | ninja-build          |\n| Library     | libcurl4-openssl-dev |\n| Library     | pkg-config           |\n| Library     | SDL2                 |\n| System Tool | 7-Zip                |\n| System Tool | Zenity               |\n\n> **Note:** In most cases, Lampray can find your 7-zip installation. However, if it's located in a non-standard location, you'll need to [manually set the path to 7-Zip](./docs/customizing-lampray.md#setting-the-path-to-7-zip).\n\n## Quick start\n\n> To build Lampray from source, see [Building from source](./docs/building-from-source.md).\n\nDownload the [latest release](https://github.com/CHollingworth/Lampray/releases) of Lampray, then move the executable to any location of your choice. For example:\n\n```bash\nmv ~/Downloads/Lampray ~\n```\n\nGive Lampray the execute permission.\n\n```bash\nchmod +x ~/Lampray\n```\n\nLaunch the Lampray application.\n\n```bash\n~/Lampray/Build/Lampray\n```\n\nThe first time you run Lampray, the following files and directories will be created:\n\nIf the CMake option `USE_XDG_DIRECTORY` is set (default):\n```bash\n~\n└── $XDG_DATA_HOME\n    ├── Archives\n    ├── Config\n    ├── Deployment\n    ├── Language\n    └── Mod_Lists\n```\nIf `USE_XDG_DIRECTORY` is not set:\n```bash\n~\n├── imgui.ini\n├── Lamp_Data\n│   ├── Archives\n│   ├── Config\n│   ├── Deployment\n│   ├── Language\n│   └── Mod_Lists\n├── lamp.log\n└── Lampray\n```\nNow you're ready to [mod your game](./docs/managing-mods.md).\n\n## Supported games\n\n### Currently supported\n\n> **Note:** At this time, mods using `.rar` files are not supported.\n\n- Baldur's Gate 3\n- Cyberpunk 2077\n\n### Planned support\n\n- The Elder Scrolls V: Skyrim (SE and AE)\n- Elder Scrolls IV: Oblivion\n- Elder Scrolls III: Morrowind\n- Fallout 4\n- The Sims 4\n\n## Current goals\n\n### Bethesda support\n\n- [ ] Loot Lib Integration\n- [ ] FoMod Parsing\n- [ ] BSA/ESP Archive Metadata collection\n\n## Become a donor\n\nIf you love using Lampray, consider supporting the project by donating to the development team. For more information, see [our page on Ko-fi](https://ko-fi.com/lampray).\n\n## Special thanks\n\nSpecial thanks to [airtonix](https://github.com/airtonix) and [SnazzyPanda](https://github.com/SnazzyPanda) and the following members of our community:\n\n- alterNERDtive\n- Azurion42\n- LT-Batman\n- pepper-jk\n- GraithTiger\n- StuffOfSonny\n- nick-scott\n- Unhall0w3d\n- Jinxtaposition\n- w0rldbuilder\n\nYour contributions have been invaluable. May this list continue to grow!\n"
  },
  {
    "path": "VERSION",
    "content": "x-release-please-start-version\n1.3.2\nx-release-please-end"
  },
  {
    "path": "build.sh",
    "content": "#!/bin/bash\n\nBUILD_TYPE=${1:-Debug}\n\ncmake \\\n    -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \\\n    -DCMAKE_MAKE_PROGRAM=ninja \\\n    -B ./build \\\n    -G Ninja \\\n    -S ./\n\n# do ninja things\ncd ./build || exit 1 # exit if build directory doesn't exist\nninja\n\necho \"📦 Build complete\""
  },
  {
    "path": "docs/building-from-source.md",
    "content": "# Building from source\n\n> Learn how to build Lampray from source. When you're finished, you'll be able to [mod your games using Lampray](./managing-mods.md).\n\n## Step 1: Clone the repository\n\nTo clone the Lampray repository into your home directory, run the following command:\n\n```\ngit clone git@github.com:CHollingworth/Lampray.git ~\n```\n\n## Step 2: Install dependencies\n\nLampray requires the following:\n\n| Type        | Name                 |\n|-------------|----------------------|\n| Build Tool  | C++                  |\n| Build Tool  | CMake                |\n| Build Tool  | GCC                  |\n| Build Tool  | ninja-build          |\n| Library     | libcurl4-openssl-dev |\n| Library     | pkg-config           |\n| Library     | SDL2                 |\n| System Tool | 7-Zip                |\n| System Tool | Zenity               |\n\n> **Note:** In most cases, Lampray can find your 7-zip installation. However, if it's located in a non-standard location, you'll need to [manually set the path to 7-Zip](./customizing-lampray.md#setting-the-path-to-7-zip).\n\nYou can either these dependencies manually or use the included setup script. To use the setup script, run the following command:\n\n```bash\n~/Lampray/setup.sh\n```\n\nIf your setup is successful, you'll see the following output:\n\n```bash\n==> 💁 [ASDF] Done ✅\n```\n\n## Step 3: Build from source\n\nYou can build Lampray by [using the Lampray build script](#build-script-recommended) or by [manually running commands](#manual).\n\n### Build script (recommended)\n\nTo build Lampray using the included build script, run the following command:\n\n```bash\n~/Lampray/build.sh\n```\n\nIf your build is successful, you'll see the following output:\n\n```\n📦 Build complete\n```\n\nFinally, launch Lampray.\n\n```bash\n~/Lampray/Build/Lampray\n```\n\n### Manual\n\nTo generate and configure Lampray's build files manually, run the following command:\n\n```bash\ncmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S ./ -B ./Build\n```\n\nOpen the newly created `Build` directory.\n\n```bash\ncd Build\n```\n\nBuild the Lampray executable.\n\n```bash\nninja\n```\n\nFinally, launch Lampray.\n\n```bash\n~/Lampray/Build/Lampray\n```\n"
  },
  {
    "path": "docs/customizing-lampray.md",
    "content": "# Customizing Lampray\n\n> Learn how to customize Lampray's fonts, transparency, color theme, and more.\n\n## Prerequisites\n\nBefore you can customize Lampray, you'll need to complete one of the following:\n\n- [Download the latest release](../README.md#quick-start)\n- [Build from source](./building-from-source.md).\n\n## Setting the path to 7-Zip\n\nIn most cases, Lampray can find your 7-Zip installation automatically. However, if it's located in a non-standard location, you'll need to manually set the path to `7z.so` in Lampray.\n\nFirst open `~/Lampray/Config/config.mdf` in your text editor, then find and replace the following line with the path to your `7z.so` utility.\n\n```sql\n<bit7zLibraryLocation>/usr/lib/p7zip/7z.so</bit7zLibraryLocation>\n```\n\n## Customizing your font\n\nYou can customize your font by creating a `Lamp_Font` directory within `Lamp_Data` and adding a `.ttf` file. Your file tree should be similar to the following:\n\n```bash\nLamp_Data\n├── Archives\n├── Config\n├── Deployment\n├── Lamp_Font \n│   └── terminus.ttf\n└── Mod_Lists\n```\n\n## Customizing your color theme\n\nYou can customize your color theme by editing `Lamp_Data/Config/Conf.mdf`.\n\n### Pre-made Colour Schemes\n\n#### Campfire (by Invisable Friend)\n```sql\nColour_Text=#FBF1C7FF\nColour_WindowBackground=#282828FF\nColour_ToolTipAndTypes=#282828FF\nColour_InputBG=#181818FF\nColour_MenuBar=#1D2021FF\nColour_Button=#D65D0EFF\nColour_Hover=#FE8019FF\nColour_Pressed=#AF3A03FF\nColour_MenuItems=#282828FF\nColour_HeadHover=#504945FF\nColour_HeadPressed=#928374FF\nColour_Separator=#282828FF\nColour_SeparatorHover=#282828FF\nColour_SearchHighlight=#504945FF\nColour_ButtonAlt=#98971AFF\n```\n#### Light Mode\n\n```sql\n<Colour_WindowBackground>efefef-ff</Colour_WindowBackground>\n<Colour_MenuBar>dbdbdb-ff</Colour_MenuBar>\n<Colour_Text>000000-ff</Colour_Text>\n<Colour_Button>4296f9-66</Colour_Button>\n<Colour_Hover>4296f9-ff</Colour_Hover>\n<Colour_Pressed>0f87f9-ff</Colour_Pressed>\n<Colour_InputBG>ffffff-ff</Colour_InputBG>\n<Colour_Separator>636363-9e</Colour_Separator>\n<Colour_SearchHighlight>4c004c-ff</Colour_SearchHighlight>\n<Colour_ToolTipAndTypes>ffffff-f9</Colour_ToolTipAndTypes>\n```\n\n#### Warlock Purple\n\n```sql\n<Colour_SearchHighlight>a61103-ff</Colour_SearchHighlight>\n<Colour_Text>ffffff-ff</Colour_Text>\n<Colour_WindowBackground>0d0f12-ff</Colour_WindowBackground>\n<Colour_ToolTipAndTypes>141414-ff</Colour_ToolTipAndTypes>\n<Colour_InputBG>2a0033-ff</Colour_InputBG>\n<Colour_MenuBar>3a196d-ff</Colour_MenuBar>\n<Colour_Button>300159-ff</Colour_Button>\n<Colour_Hover>6703a6-ff</Colour_Hover>\n<Colour_Pressed>260101-ff</Colour_Pressed>\n<Colour_MenuItems>5f80a7-4f</Colour_MenuItems>\n<Colour_HeadHover>4296f9-cc</Colour_HeadHover>\n<Colour_HeadPressed>4296f9-ff</Colour_HeadPressed>\n<Colour_Separator>2a0133-ff</Colour_Separator>\n<Colour_SeparatorHover>1966bf-c6</Colour_SeparatorHover>\n```\n\n#### Discord's Clyde's Ai Generated Scheme\n\n```sql\n<Colour_WindowBackground>51216B-ef</Colour_WindowBackground>\n<Colour_MenuBar>EFD951-ff</Colour_MenuBar>\n<Colour_Text>ffffff-ff</Colour_Text>\n<Colour_Button>75EF51-66</Colour_Button>\n<Colour_Hover>75EF51-ff</Colour_Hover>\n<Colour_Pressed>EF7551-ff</Colour_Pressed>\n<Colour_InputBG>5151EF-89</Colour_InputBG>\n<Colour_Separator>EF5151-7f</Colour_Separator>\n<Colour_SearchHighlight>4c004c-ff</Colour_SearchHighlight>\n<Colour_ToolTipAndTypes>000CF2-ef</Colour_ToolTipAndTypes>\n```\n"
  },
  {
    "path": "docs/directory-structure.md",
    "content": "# Directory structure\n\n> These are the various directories that make up Lampray.\n\n## Overview\n\nThese are the primary directories you'll use to manage and configure Lampray.\n\n```bash\nLampray\n└── Build\n    └── Lamp_Data \n        ├── Archive\n        ├── Config\n        └── Mod_Lists \n```\n\n## `Lamp_Data`\n\nThe directory where Lampray stores all the necessary files needed for deployment before you [deploy your mods](managing-mods.md#adding-mods). This directory is fully managed by Lampray, so there's no need to modify this directory or its contents.\n\n## `Archives`\n\nThe directory that stores your mods' `.zip` files when you drag and drop your mods' `.zip` files into Lampray. This directory is fully managed by Lampray, so there's no need to modify this directory or its contents.\n\n## `Config`\n\nThe directory that stores your Lampray configuration files, such as `conf.mdf` which is used to [customize Lampray](customizing-lampray.md).\n\n## `Mod_Lists`\n\nThe directory containing your games' `.mdf` files which lists the installed game, its mods, and the corresponding [mod types](./mod-types/supported-games.md). This directory is fully managed by Lampray, so there's no need to modify this directory or its contents.\n"
  },
  {
    "path": "docs/frequently-asked-questions.md",
    "content": "# Frequently Asked Questions (FAQ) \n\n> These are the frequently asked questions for Lampray. This list will continue to be updated over time.\n\n### Q: Why is Lampray not opening?\n\n**A:** Make sure all the [dependencies are installed](../README.md#dependencies). If your error persists, [create a GitHub issue](https://github.com/CHollingworth/Lampray/issues/new?assignees=&labels=bug&projects=&template=-game--bug-report.md&title=) and send us your terminal output.\n\n### Q: Why is \"Deploy\" not installing my mods?\n\n**A:** Lampray may be having difficulty locating 7-Zip. To set the path to `7z.so` manually, see [Customizing Lampray](./customizing-lampray.md#setting-the-path-to-7-zip). \n\n### Q: My deployment gets stuck at 0/0\n\n**A:** Youll need to set your config paths in the game configuration menu. If your error persists, [create a GitHub issue](https://github.com/CHollingworth/Lampray/issues/new?assignees=&labels=bug&projects=&template=-game--bug-report.md&title=) and send us your terminal output.\n\n### Q: Where can I find Canary updates or pre-releases they are no longer on GitHub?\n\n**A:** You can find these in [our Discord](https://discord.gg/5macMedevy).\n"
  },
  {
    "path": "docs/lampray-docs.md",
    "content": "# Lampray Docs\n\nWelcome to Lampray Docs! Whether you're new to Lampray or a long-time user, the docs are a valuable way to learn how to build, use, and customize Lampray. If you'd like to help improve the docs, you can [report an issue](https://github.com/CHollingworth/Lampray/issues/new?assignees=&labels=bug&projects=&template=-game--bug-report.md&title=) or [request a feature](https://github.com/CHollingworth/Lampray/issues/new?assignees=&labels=enhancement&projects=&template=feature_request.md&title=).\n\n## In this section:\n\n- [Building from source](./building-from-source.md)\n- [Managing mods](./managing-mods.md)\n- [Customizing Lampray](./customizing-lampray.md)\n- **Mod Types**\n  - [Supported games](./mod-types/supported-games.md) \n  - [Balder's Gate 3](mod-types/baldurs-gate-3.md)\n  - [Cyberpunk 2077](./mod-types/cyberpunk-2077.md)\n- [Directory structure](directory-structure.md)\n- [Frequently Asked Questions (FAQ)](./frequently-asked-questions.md)\n"
  },
  {
    "path": "docs/managing-mods.md",
    "content": "# Managing mods\n\n> Learn how to manage your mods using Lampray.\n\n## Prerequisites\n\nBefore you can manage your mods using Lampray, you'll need to complete one of the following:\n\n- [Download the latest release](../README.md#quick-start)\n- [Build from source](./building-from-source.md).\n\n## Step 1: Set your game paths\n\nYou'll need to set the path to your Steam library and game data in Lampray, so your mods are added to the right directories.\n\nAt the top of the window, select **Lampray** > **Steam Directory**, then follow the on-screen instructions to set the path to your Steam game's root directory and `AppData` directory.\n\nFor Steam Deck you need either to connect a keyboard or install **CoreKeyboard** in desktop mode via Discover app (it can be found in the bottom left part of the screen, it has a blue bag icon). To set the path to the game you need to click `Lampray - Baldur's Gate 3 - Default` in the top left corner with your right trackpad and choose `Game Configuration`.  \n\nThis is where you need to use CoreKeyboard. Press Ctrl + H to see hidden folders and files. Current paths for Baldur's Gate are:\n` /home/deck/.steam/steamapps/common/Baldurs Gate 3`\nand\n`/home/deck/.steam/steamapps/compdata/1086940/pfx/drive_c/users/steamuser/AppData/Local/Larian Studios/Baldur’s Gate 3`.\n\n## Step 2: Manage your mods\n\nYou'll need to complete the following steps anytime you add or remove mods using Lampray:\n\n### Adding mods\n\n> **Note:** At this time, mods using `.rar` files are not supported.\n\n1. Download your mods from a mod repository, such as Nexus Mods.\n2. At the top corner, choose the game you'd like to mod.\n3. Drag and drop the `.zip` packages for each mod into Lampray.\n4. Enable each mod, choose their mod type, then set their load order. For more information about each mod type, see [Mod types](./mod-types/supported-games.md). \n5. Select **Deploy**.\n6. When Lampray is finished deploying your mods, exit Lampray, then launch your game.\n\n### Removing mods\n\n1. At the top corner, choose the game you'd like to remove a mod from.\n2. For each mod you'd like to remove, select **Disable**, then **Remove Mod**.\n3. When you're finished, select **Deploy**.\n"
  },
  {
    "path": "docs/mod-types/baldurs-gate-3.md",
    "content": "# Baldur's Gate 3 mod types\n\n> These are the various mod types you can select when [modding your game](../managing-mods.md) for Baldur's Gate 3 (BG3).\n\n## Standard Mods\n\nMods using a single `.pak` file. Typically, these are mods that are dependent on other mods.\n\n### Example\n\n**Deployed from:**\n\n```bash\n~/Lamp_Data/Deployment/Baldur's Gate 3/Mods\n```\n\n**Deployed to:**\n\n```bash\n~/users/steamuser/Appdata/Local/Larian Studios/Baldur's Gate 3/Mods\n```\n\n## Engine Injection \n\nMods containing DLL's that need to be loaded with the game via NativeModLoader. \n\n### Example\n\n**Deployed from:**\n\n```bash\n~/Lamp_Data/Deployment/Baldur's Gate 3/bin/NativeMods\n```\n\n**Deployed to:**\n\n```bash\n~/.steam/debian-installation/steamapps/common/Baldurs Gate 3/bin/NativeMods\n```\n\n## Bin Overwrite\n\nMods that directly overwrite files in the `/BG3/bin` directory, such as mods that overwrite an executable or core DLLs. (Only a few mods will ever need this.) _(Lampray cannot undo This operation)._\n\n### Example\n\n**Deployed from:**\n\n```bash\n~/Lamp_Data/Deployment/Baldur's Gate 3/bin\n```\n\n**Deployed to:**\n\n```bash\n~/.steam/debian-installation/steamapps/common/Baldurs Gate 3/bin\n```\n\n## Data Overwrite\n\nMods containing loose files, such as textures or meshes for characters and objects. To verify, check if the mod's `.zip` file contains multiple loose files or a single `.pak` file. If it contains loose files, choose **Data Overwrite**. _(Lampray cannot undo This operation)._\n\n### Example\n\n**Deployed from:**\n\n```bash\n~/Lamp_Data/Deployment/Baldur's Gate 3/Data\n```\n\n**Deployed to:**\n\n```bash\n~/.steam/debian-installation/steamapps/common/Baldurs Gate 3/Data/Generated/Public/SharedDev/Assets/Characters/_Models/path/to/some/modelfile\n```\n\n## No Json Mod \n\nMods that contain a single `.pak` file, but **do not** depend on other mods, such as the ImprovedUI, ModFixer, and Better Containers mods.\n\n### Example\n\n**Deployed from:**\n\n```bash\n~/Lamp_Data/Deployment/Baldur's Gate 3/Mods\n```\n\n**Deployed to:**\n\n```bash\n~/.steam/debian-installation/steamapps/compatdata/1086940/pfx/drive_c/users/steamuser/AppData/Local/Larian Studios/Baldurs Gate 3/Mods\n```\n"
  },
  {
    "path": "docs/mod-types/cyberpunk-2077.md",
    "content": "# Cyberpunk 2077 mod types\n\n> **Note:** This page is currently under construction. Check back again soon!\n"
  },
  {
    "path": "docs/mod-types/supported-games.md",
    "content": "# Supported games\n\nThese are the games Lampray currently supports. Select a game below to learn more about its specific mod types:\n\n- [Baldur's Gate 3](./baldurs-gate-3.md)\n- [Cyberpunk 2077](./cyberpunk-2077.md)\n"
  },
  {
    "path": "game-data/BG3/BG3.cpp",
    "content": "//\n// Created by charles on 27/09/23.\n//\n\n#include <filesystem>\n#include <regex>\n#include \"BG3.h\"\n#include \"../../Lampray/Control/lampControl.h\"\n#include \"../../third-party/json/json.hpp\"\n\nLamp::Game::lampReturn Lamp::Game::BG3::registerArchive(Lamp::Game::lampString Path, int ArchiveModType) {\n    if(ArchiveModType < 0){\n        ArchiveModType = NaN;\n    }\n\n    // skip the filename checks for mod separators\n    if(ArchiveModType != MOD_SEPARATOR){\n        for (Core::Base::lampMod::Mod* it : ModList) {\n\n            std::filesystem::path NewFilePath = Path;\n            std::filesystem::path TestingAgainstPath = it->ArchivePath;\n\n\n            std::string NewFilePathCut = NewFilePath.filename();\n            /*\n            size_t posA = NewFilePathCut.find('-');\n            if (posA != std::string::npos) {\n                NewFilePathCut.erase(posA);\n            }\n            */\n\n            std::string TestingAgainstPathCut = TestingAgainstPath.filename();\n            size_t posB = TestingAgainstPathCut.find('/');\n            if (posB != std::string::npos) {\n                TestingAgainstPathCut.erase(posB);\n            }\n\n\n            if(NewFilePathCut == TestingAgainstPathCut){\n\n                it->timeOfUpdate = Lamp::Core::lampControl::getFormattedTimeAndDate();\n                it->ArchivePath = Path;\n                return Lamp::Core::FS::lampIO::saveModList(Ident().ShortHand,ModList,Games::getInstance().currentProfile);\n            }\n        }\n    }\n\n    Core::Base::lampMod::Mod  * newArchive = new Core::Base::lampMod::Mod{Path, ArchiveModType, false};\n    newArchive->timeOfUpdate = Lamp::Core::lampControl::getFormattedTimeAndDate();\n    ModList.push_back(newArchive);\n    return Lamp::Core::FS::lampIO::saveModList(Ident().ShortHand,ModList,Games::getInstance().currentProfile);\n}\n\nLamp::Game::lampReturn Lamp::Game::BG3::ConfigMenu() {\n    ImGui::Separator();\n    Lamp::Core::lampControl::lampGameSettingsDisplayHelper(\"BG3 Steam Directory\", keyInfo[\"installDirPath\"],\n                                                           \"This is usually (steampath)/steamapps/common/Baldurs Gate 3\",\n                                                            \"installDirPath\").createImGuiMenu();\n    ImGui::Separator();\n    Lamp::Core::lampControl::lampGameSettingsDisplayHelper(\"BG3 AppData Directory\", keyInfo[\"appDataPath\"],\n                                                           \"This is usually (steampath)/steamapps/compatdata/1086940/pfx/drive_c/users/steamuser/AppData/Local/Larian Studios/Baldur's Gate 3\",\n                                                           \"appDataPath\").createImGuiMenu();\n    return false;\n}\n\nLamp::Game::lampReturn Lamp::Game::BG3::startDeployment() {\n    Lamp::Core::lampControl::getInstance().inDeployment = true;\n    Lamp::Core::FS::lampTrack::reset(Ident().ReadableName);\n    if(KeyInfo()[\"installDirPath\"] == \"\" | KeyInfo()[\"appDataPath\"] == \"\" ) {\n        return Core::Base::lampLog::getInstance().pLog({0, \"Game Configuration not set.\"}, Core::Base::lampLog::warningLevel::WARNING, true, Core::Base::lampLog::LMP_NOCONFIG);\n    }\n\n    Core::Base::LampSequencer::add(\"BG3 Deployment Queue\", [this]() -> lampReturn {\n        Lamp::Core::lampControl::getInstance().deploymentStageTitle = \"Preparing\";\n        auto result = preCleanUp();\n        if(!result) Core::Base::lampLog::getInstance().log(\"Pre cleanup has failed.\", Core::Base::lampLog::warningLevel::ERROR, true, Core::Base::lampLog::LMP_CLEANUPFAILED);\n        return result;\n    });\n\n    Core::Base::LampSequencer::add(\"BG3 Deployment Queue\", [this]() -> lampReturn {\n        Lamp::Core::lampControl::getInstance().deploymentStageTitle = \"Pre Deployment\";\n        auto result = preDeployment();\n        if(!result) Core::Base::lampLog::getInstance().log(\"Pre Deployment has failed.\", Core::Base::lampLog::warningLevel::ERROR, true, Core::Base::lampLog::LMP_PREDEPLOYFAILED);\n        return result;\n    });\n\n\n    Core::Base::LampSequencer::add(\"BG3 Deployment Queue\", [this]() -> lampReturn {\n        Lamp::Core::lampControl::getInstance().deploymentStageTitle = \"Deploying\";\n        auto result = deployment();\n        if(!result) Core::Base::lampLog::getInstance().log(\"Deployment has failed.\", Core::Base::lampLog::warningLevel::ERROR, true, Core::Base::lampLog::LMP_DEOPLYMENTFAILED);\n        return result;\n    });\n\n\n    Core::Base::LampSequencer::run(\"BG3 Deployment Queue\");\n    Lamp::Core::lampControl::getInstance().inDeployment = false;\n    return false;\n}\n\nLamp::Game::lampReturn Lamp::Game::BG3::preCleanUp() {\n\n    std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath + Ident().ReadableName;\n    Lamp::Core::lampControl::getInstance().deplopmentTracker = {0,6};\n\n    std::filesystem::path dir(workingDir);\n\n    Core::Base::lampLog::getInstance().log(\"Cleaning Working Directory : \"+workingDir, Core::Base::lampLog::warningLevel::LOG);\n    try {\n        for (const auto &entry: std::filesystem::directory_iterator(dir)) {\n            if(entry.is_directory()){\n               for(const auto &sub_entry: std::filesystem::directory_iterator(entry)){\n                   std::filesystem::remove_all(sub_entry.path());\n               }\n            }else {\n                std::filesystem::remove_all(entry.path());\n            }\n        }\n    }catch(std::exception e){\n        return {-1, \"Cannot clean working directories\"};\n    }\n\n    Lamp::Core::lampControl::getInstance().deplopmentTracker.first = 1;\n    // Phantom Step.\n    Lamp::Core::lampControl::getInstance().deplopmentTracker.first = 2;\n    Core::Base::lampLog::getInstance().log(\"Creating Working Directories\", Core::Base::lampLog::warningLevel::LOG);\n    try {\n        std::filesystem::create_directories(workingDir + \"/Steam/bin/NativeMods\");\n        std::filesystem::create_directories(workingDir + \"/Steam/Data\");\n        std::filesystem::create_directories(workingDir + \"/Mods\");\n        std::filesystem::create_directories(workingDir + \"/PlayerProfiles/Public\");\n        std::filesystem::create_directories(workingDir + \"/OverlayConfigLayer\");\n        std::filesystem::create_directories(workingDir + \"/ext\");\n    }catch(std::exception e){\n        return {-1, \"Unable to create working directories.\"};\n    }\n    Lamp::Core::lampControl::getInstance().deplopmentTracker.first = 3;\n    Core::Base::lampLog::getInstance().log(\"Creating modsettings.lsx\", Core::Base::lampLog::warningLevel::LOG);\n    pugi::xml_document doc;\n\n\n\n    pugi::xml_node save = doc.append_child(\"save\");\n\n    // Create the version node\n    pugi::xml_node version = save.append_child(\"version\");\n    version.append_attribute(\"major\").set_value(4);\n    version.append_attribute(\"minor\").set_value(7);\n    version.append_attribute(\"revision\").set_value(1);\n    version.append_attribute(\"build\").set_value(3);\n\n    // Create the region node\n    pugi::xml_node region = save.append_child(\"region\");\n    region.append_attribute(\"id\").set_value(\"ModuleSettings\");\n\n    // Create the node node\n    pugi::xml_node node = region.append_child(\"node\");\n    node.append_attribute(\"id\").set_value(\"root\");\n\n    // Create the children node\n    pugi::xml_node children = node.append_child(\"children\");\n\n    // Create the Mods node\n    pugi::xml_node mods = children.append_child(\"node\");\n    mods.append_attribute(\"id\").set_value(\"Mods\");\n\n    // Create the children node for Mods (empty in your example)\n    pugi::xml_node modsChildren = mods.append_child(\"children\");\n\n    Lamp::Core::lampControl::getInstance().deplopmentTracker.first = 4;\n    Core::Base::lampLog::getInstance().log(\"Cleaning modsettings.lsx\", Core::Base::lampLog::warningLevel::LOG);\n    pugi::xml_node modOrderNode = doc.select_node(\"//node[@id='Mods']\").node();\n    pugi::xml_node modsNode = doc.select_node(\"//node[@id='Mods']\").node();\n\n    if (modOrderNode) {\n        pugi::xml_node childrenNode = modOrderNode.child(\"children\");\n        if (childrenNode) {\n            childrenNode.remove_children();\n        }else{\n            modOrderNode.append_child(\"children\");\n        }\n    } else {\n        return {-1, \"Mods section not found.\"};\n    }\n    if (modsNode) {\n        pugi::xml_node childrenNode = modsNode.child(\"children\");\n        if (childrenNode) {\n            childrenNode.remove_children();\n        }else{\n            modsNode.append_child(\"children\");\n        }\n    } else {\n        return {-1, \"Mods section not found.\"};\n    }\n    Lamp::Core::lampControl::getInstance().deplopmentTracker.first = 5;\n    Core::Base::lampLog::getInstance().log(\"Injecting GustavDev into modsettings.lsx\", Core::Base::lampLog::warningLevel::LOG);\n    pugi::xml_node modsNode2 = doc.select_node(\"//node[@id='Mods']\").node();\n    pugi::xml_node childrenNode = modsNode2.child(\"children\");\n\n    pugi::xml_node newShortDescNode = childrenNode.append_child(\"node\");\n    newShortDescNode.append_attribute(\"id\") = \"ModuleShortDesc\";\n\n    pugi::xml_node folderAttrib = newShortDescNode.append_child(\"attribute\");\n    folderAttrib.append_attribute(\"id\") = \"Folder\";\n    folderAttrib.append_attribute(\"type\") = \"LSString\";\n    folderAttrib.append_attribute(\"value\") = \"GustavDev\";\n\n    pugi::xml_node md5Attrib = newShortDescNode.append_child(\"attribute\");\n    md5Attrib.append_attribute(\"id\") = \"MD5\";\n    md5Attrib.append_attribute(\"type\") = \"LSString\";\n    md5Attrib.append_attribute(\"value\") = \"\";\n\n    pugi::xml_node nameAttrib = newShortDescNode.append_child(\"attribute\");\n    nameAttrib.append_attribute(\"id\") = \"Name\";\n    nameAttrib.append_attribute(\"type\") = \"LSString\";\n    nameAttrib.append_attribute(\"value\") = \"GustavDev\";\n\n    pugi::xml_node uuidAttrib = newShortDescNode.append_child(\"attribute\");\n    uuidAttrib.append_attribute(\"id\") = \"UUID\";\n    uuidAttrib.append_attribute(\"type\") = \"guid\";\n    uuidAttrib.append_attribute(\"value\") = \"28ac9ce2-2aba-8cda-b3b5-6e922f71b6b8\";\n\n\n    pugi::xml_node versionAttrib = newShortDescNode.append_child(\"attribute\");\n    versionAttrib.append_attribute(\"id\") = \"Version64\";\n    versionAttrib.append_attribute(\"value\") = \"36028797018963968\";\n    versionAttrib.append_attribute(\"type\") = \"int64\";\n\n    Core::Base::lampLog::getInstance().log(\"Saving modsettings.lsx\", Core::Base::lampLog::warningLevel::LOG);\n    if (!doc.save_file((workingDir + \"/PlayerProfiles/Public/modsettings.lsx\").c_str())) {\n        return {-1, \"Failed to save modsettings.lsx\"};\n    }\n\n    Lamp::Core::lampControl::getInstance().deplopmentTracker.first = 6;\n\n    std::filesystem::path installPath(keyInfo[\"installDirPath\"]);\n    if (std::filesystem::exists(installPath) && std::filesystem::is_directory(installPath) &&\n            std::filesystem::exists(installPath.parent_path() / (\"Lampray Managed - \" + installPath.stem().string())) &&\n            std::filesystem::is_directory(installPath.parent_path() / (\"Lampray Managed - \" + installPath.stem().string()))) {\n        if(std::filesystem::is_empty(installPath)){\n            system((\"pkexec umount \\\"\"+Lamp::Games::getInstance().currentGame->KeyInfo()[\"installDirPath\"]+\"\\\"\").c_str());\n            try {\n                std::filesystem::rename(\n                        installPath.parent_path() / (\"Lampray Managed - \" + installPath.stem().string()),\n                        keyInfo[\"installDirPath\"]);\n            } catch (std::exception ex) {\n\n            }\n\n            skipMount = false;\n        } else {\n            skipMount = true;\n        }\n    }\n    \n    std::filesystem::path appPath(keyInfo[\"appDataPath\"]);\n    std::filesystem::path tempAppPath(appPath.parent_path() / (\"Lampray Managed - \" + appPath.stem().string()));\n    \n    if(std::filesystem::exists(appPath) && std::filesystem::is_directory(appPath)\n        && std::filesystem::exists(tempAppPath) && std::filesystem::is_directory(tempAppPath)) {\n        \n        if(std::filesystem::is_empty(std::filesystem::path(KeyInfo()[\"appDataPath\"]+\"/Mods\"))){\n            system((\"pkexec umount \\\"\"+Lamp::Games::getInstance().currentGame->KeyInfo()[\"appDataPath\"]+\"/Mods\\\"\").c_str());\n            try {\n                std::filesystem::rename(tempAppPath / \"Mods\",\n                                        appPath / \"Mods\");\n            } catch (std::exception ex) {\n\n            }\n            skipMount = false;\n        } else {\n          skipMount = true;\n        }\n    }\n\n//    std::string managedString = std::string(\"Lampray Managed - \") + gamePath.filename().string();\n//    std::filesystem::path MergedPath = gamePath.parent_path() / managedString;\n//    std::filesystem::rename(gamePath, MergedPath);\n//    std::filesystem::create_directories(gamePath);\n\n    return {1, \"PreCleanup Finished.\"};\n}\n\nLamp::Game::lampReturn Lamp::Game::BG3::preDeployment() {\n    Lamp::Core::lampControl::getInstance().deplopmentTracker = {0,0};\n    Core::Base::lampLog::getInstance().log(\"Extracting Archives\", Core::Base::lampLog::warningLevel::LOG);\n    auto lambdaFunction = [](const Core::Base::lampMod::Mod* item) {\n        if(item->enabled) {\n            Lamp::Core::lampControl::getInstance().deplopmentTracker.second++;\n        if(Lamp::Core::FS::lampExtract::extract(item)) {\n\n            std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath + Lamp::Games::getInstance().currentGame->Ident().ReadableName;\n            std::filesystem::path dirit(workingDir + \"/ext/\" + std::filesystem::path(item->ArchivePath).filename().stem().string());\n\n            try {\n                for (const auto &entry: std::filesystem::directory_iterator(dirit)) {\n                    if (entry.is_regular_file()) {\n                        std::string fileName = entry.path().filename().string();\n                        if (fileName.find('\\\\') != std::string::npos) {\n                            std::string inputString = fileName;\n                            size_t found = inputString.find('\\\\');\n                            while (found != std::string::npos) {\n                                inputString.replace(found, 1, \"/\");\n                                found = inputString.find('\\\\', found + 1);\n                            }\n                            std::filesystem::path temp(inputString);\n                            std::filesystem::rename(entry.path(),\n                                                    workingDir +  \"/ext/\" + std::filesystem::path(item->ArchivePath).filename().stem().string() + \"/\" + temp.filename().string());\n                            std::cout << \"File with backslash in name: \" << fileName << std::endl;\n                        }\n                    }\n                }\n            } catch (const std::exception &e) {\n                std::cerr << \"Error: \" << e.what() << std::endl;\n            }\n\n\n\n\n            std::string x = item->ArchivePath;\n            switch (item->modType) {\n                case BG3_ENGINE_INJECTION:\n                    Lamp::Core::FS::lampExtract::moveModSpecificFileType(item,\"dll\",\"Steam/bin/NativeMods\");\n                    Lamp::Core::FS::lampExtract::moveModSpecificFileType(item,\"toml\",\"Steam/bin/NativeMods\");\n                    Lamp::Core::FS::lampExtract::moveModSpecificFileType(item,\"ini\",\"Steam/bin/NativeMods\");\n                    break;\n                case BG3_MOD:\n                    Lamp::Core::FS::lampExtract::moveModSpecificFileType(item,\"pak\",\"Mods\");\n\n                    Core::Base::LampSequencer::add(\"BG3 Settings Control\", [x]() -> lampReturn {\n                        if(!Lamp::Core::Parse::lampBG3PakParser::collectJsonData(x)){\n                            return Lamp::Core::Parse::lampBG3PakParser::extractJsonData(x);\n                        }else{\n                            return true;\n                        }\n                    });\n                    break;\n                case BG3_BIN_OVERRIDE:\n                    Lamp::Core::FS::lampExtract::moveModSpecificFolder(item,\"bin\",\"Steam/bin\");\n                    Lamp::Core::FS::lampExtract::moveModSpecificFileType(item, \"dll\", \"Steam/bin\");\n                    break;\n                case BG3_DATA_OVERRIDE:\n                    Lamp::Core::FS::lampExtract::moveModSpecificFolder(item,\"Data\",\"Steam/Data\");\n                    break;\n                case BG3_MOD_FIXER:\n                    Lamp::Core::FS::lampExtract::moveModSpecificFileType(item,\"pak\",\"Mods\");\n                    break;\n                case MOD_SEPARATOR:\n                    break;\n                default:\n                    break;\n            }\n            Lamp::Core::lampControl::getInstance().deplopmentTracker.first++;\n        }\n        }\n    };\n    lampExecutor executor(lambdaFunction);\n    executor.execute(ModList,false);\n    Core::Base::LampSequencer::run(\"BG3 Settings Control\");\n    return {1,\"TESTER\"};\n}\n\nLamp::Game::lampReturn Lamp::Game::BG3::deployment() {\n    std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath + Ident().ReadableName;\n    Lamp::Core::lampControl::getInstance().deplopmentTracker = {0,4};\n\n    if(!skipMount) {\n        Core::Base::OverlayBuilder *BG3Overlay = new Core::Base::OverlayBuilder;\n        std::filesystem::path SteamDataPath(workingDir + \"/Steam\");\n        std::filesystem::path SteamConfig(\n                Lamp::Core::lampConfig::getInstance().ConfigDataPath + Ident().ReadableName + \"/STEAM/\");\n        std::filesystem::path SteamWorkingDir(\n                Lamp::Core::lampConfig::getInstance().workingPaths + Ident().ReadableName + \"/STEAM/\");\n\n        BG3Overlay->addPath(absolute(SteamDataPath));\n        lampReturn SteamReturn = BG3Overlay->create(std::filesystem::absolute(keyInfo[\"installDirPath\"]),\n                                                    absolute(SteamConfig), absolute(SteamWorkingDir));\n        Core::Base::lampLog::getInstance().log(SteamReturn.returnReason, Core::Base::lampLog::warningLevel::WARNING,\n                                               false);\n\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {1,4};\n        Core::Base::OverlayBuilder *BG3OverlayMods = new Core::Base::OverlayBuilder;\n        std::filesystem::path ModsWorkingDir(\n                Lamp::Core::lampConfig::getInstance().workingPaths + Ident().ReadableName + \"/MODS/\");\n        std::filesystem::path ModsConfig(\n                Lamp::Core::lampConfig::getInstance().ConfigDataPath + Ident().ReadableName + \"/MODS/\");\n\n        std::filesystem::path ModsDataPath(workingDir + \"/Mods\");\n        BG3OverlayMods->addPath(absolute(ModsDataPath));\n        Lamp::Core::lampControl::getInstance().deplopmentTracker = {2,4};\n        Core::Base::lampLog::getInstance().log(\n                BG3OverlayMods->create(std::filesystem::absolute(KeyInfo()[\"appDataPath\"] + \"/Mods\"),\n                                       absolute(ModsConfig), absolute(ModsWorkingDir)).returnReason,\n                Core::Base::lampLog::warningLevel::WARNING, false);\n\n    }\n\n    Core::Base::lampLog::getInstance().log(\"Copying ModProfile\");\n    Lamp::Core::lampControl::getInstance().deplopmentTracker = {3,4};\n        Lamp::Core::FS::lampTrack::handleFileDescriptor E{\n                Lamp::Core::FS::lampTrack::handleFileDescriptor::operation::copyFolder,\n                Lamp::Core::FS::lampTrack::handleFileDescriptor::mode::updateExisting,\n                workingDir+\"/PlayerProfiles\",\n                KeyInfo()[\"appDataPath\"]+\"/PlayerProfiles/\",\n                \"\",\n                Ident().ReadableName\n        };\n        Lamp::Core::FS::lampTrack::handleFile(E);\n    Lamp::Core::lampControl::getInstance().deplopmentTracker = {4,4};\n}\n\nLamp::Game::lampReturn Lamp::Game::BG3::postDeploymentTasks() {\n    return false;\n}\n\nvoid Lamp::Game::BG3::listArchives() {\n    Lamp::Core::lampControl::lampArchiveDisplayHelper(\n            std::list<std::string>{},\n            ModList,\n            std::list<std::pair<std::string, bool *>>{}\n    ).createImguiMenu();\n}\n\nstd::vector<Lamp::Core::Base::lampMod::Mod *> &Lamp::Game::BG3::getModList() {\n    return ModList;\n}\n\n\n\n"
  },
  {
    "path": "game-data/BG3/BG3.h",
    "content": "//\n// Created by charles on 27/09/23.\n//\n\n#ifndef LAMP_BG3_H\n#define LAMP_BG3_H\n#include \"../gameControl.h\"\n#include \"../../Lampray/Filesystem/lampFS.h\"\n#include \"../../Lampray/Parse/lampParse.h\"\nnamespace Lamp::Game {\n    typedef Core::Base::lampTypes::lampString lampString;\n    typedef Core::Base::lampTypes::lampHexAlpha lampHex;\n    typedef Core::Base::lampTypes::lampReturn lampReturn;\n\n    class BG3 : public gameControl {\n    public:\n\n        lampReturn registerArchive(lampString Path, int ArchiveModType = -1) override;\n        lampReturn ConfigMenu() override;\n        lampReturn startDeployment() override;\n        lampReturn preCleanUp() override;\n        lampReturn preDeployment() override;\n        lampReturn deployment() override;\n        lampReturn postDeploymentTasks() override;\n        void listArchives() override;\n\n        std::vector<Core::Base::lampMod::Mod *> ModList {};\n\n        std::map<std::string,std::string>& KeyInfo() override {\n            return keyInfo;\n        };\n\n        Core::Base::lampTypes::lampIdent Ident() override {\n            return {\"Baldur's Gate 3\",\"BG3\"};\n        };\n\n        std::vector<Core::Base::lampMod::Mod *>& getModList() override;\n\n        void launch() override {\n            for (const auto& pair : keyInfo) {\n                const std::string& key = pair.first;\n                keyInfo[key] = (std::string) Lamp::Core::FS::lampIO::loadKeyData(key,Ident().ShortHand).returnReason;\n                if(key == \"ProfileList\"){\n                    if(pair.second == \"\" || pair.second == \"Default\") {\n                        keyInfo[key] = \"Default\";\n                        Lamp::Core::FS::lampIO::saveKeyData(key, keyInfo[key], Ident().ShortHand);\n                    }\n                }\n                if(key == \"CurrentProfile\"){\n                    if(pair.second == \"\" || pair.second == \"Default\") {\n                        keyInfo[key] = \"Default\";\n                        Lamp::Core::FS::lampIO::saveKeyData(key, keyInfo[key], Ident().ShortHand);\n                    }\n                }\n            }\n            ModList = Lamp::Core::FS::lampIO::loadModList(Ident().ShortHand, keyInfo[\"CurrentProfile\"]);\n        }\n\n        int SeparatorModType(){\n            return MOD_SEPARATOR;\n        }\n\n        std::vector<std::pair<int, std::string> >& getModTypes() override {\n            return ModTypeList;\n        }\n\n        std::map<int, std::string>& getModTypesMap() override{\n            return ModTypeMap;\n        }\n\n        void unmount() override{\n            std::filesystem::path installPath(KeyInfo()[\"installDirPath\"]);\n            system((\"pkexec umount \\\"\"+KeyInfo()[\"installDirPath\"]+\"\\\"\").c_str());\n            std::filesystem::rename(installPath.parent_path() / (\"Lampray Managed - \" + installPath.stem().string()), KeyInfo()[\"installDirPath\"]);\n            system((\"pkexec umount \\\"\"+KeyInfo()[\"appDataPath\"]+\"/Mods\\\"\").c_str());\n            std::filesystem::rename(std::filesystem::path(KeyInfo()[\"appDataPath\"]+\"/Mods\").parent_path() / (\"Lampray Managed - \" + std::filesystem::path(KeyInfo()[\"appDataPath\"]+\"/Mods\").stem().string()), std::filesystem::path(KeyInfo()[\"appDataPath\"]+\"/Mods\"));\n        }\n\n        bool installPathSet() override{\n            if(this->KeyInfo()[\"installDirPath\"] == \"\" || this->KeyInfo()[\"appDataPath\"] == \"\"){\n                return false;\n            }\n            return true;\n        }\n\n\tprivate:\n\n        bool skipMount = false;\n\n        enum ModType{\n            BG3_ENGINE_INJECTION = 0,\n            BG3_MOD,\n            BG3_BIN_OVERRIDE,\n            BG3_DATA_OVERRIDE,\n            BG3_MOD_FIXER,\n            NaN,\n            MOD_SEPARATOR = 999,\n        };\n\n        // use a vector to keep things organized, this allows us to output mod types in the order we define\n        std::vector<std::pair<int, std::string> > ModTypeList{\n            { BG3_ENGINE_INJECTION, \"Engine Injection\" },\n            { BG3_MOD, \"Standard Mod\" },\n            { BG3_BIN_OVERRIDE, \"Bin Overwrite\" },\n            { BG3_DATA_OVERRIDE, \"Data Overwrite\" },\n            { BG3_MOD_FIXER, \"No Json Mod\" },\n            { NaN, \"Select Type\" },\n            { MOD_SEPARATOR, \"Separator\" },\n        };\n        // we will load the mod type vector above into this so we can get display values by the mod type value\n        std::map<int, std::string> ModTypeMap = initModTypesMap();\n\n\n        std::map<std::string,std::string> keyInfo{\n                {\"installDirPath\",\"\"},\n                {\"appDataPath\",\"\"},\n                {\"ProfileList\",\"Default\"},\n                {\"CurrentProfile\", \"Default\"}\n        };\n    };\n}\n\n#endif //LAMP_BG3_H\n"
  },
  {
    "path": "game-data/C77/C77.cpp",
    "content": "//\n// Created by charles on 14/10/23.\n//\n\n#include \"C77.h\"\n#include \"../../Lampray/Control/lampControl.h\"\nnamespace Lamp {\n    namespace Game {\n        lampReturn C77::registerArchive(lampString Path, int ArchiveModType) {\n            if(ArchiveModType < 0){\n                ArchiveModType = C77_MOD;\n            }\n\n            if(ArchiveModType != MOD_SEPARATOR){\n                for (Core::Base::lampMod::Mod* it : ModList) {\n\n                    std::filesystem::path NewFilePath = Path;\n                    std::filesystem::path TestingAgainstPath = it->ArchivePath;\n\n\n                    std::string NewFilePathCut = NewFilePath.filename();\n                    /*\n                    size_t posA = NewFilePathCut.find('-');\n                    if (posA != std::string::npos) {\n                        NewFilePathCut.erase(posA);\n                    }\n                    */\n\n                    std::string TestingAgainstPathCut = TestingAgainstPath.filename();\n                    size_t posB = TestingAgainstPathCut.find('/');\n                    if (posB != std::string::npos) {\n                        TestingAgainstPathCut.erase(posB);\n                    }\n\n\n                    if(NewFilePathCut == TestingAgainstPathCut){\n\n                        it->timeOfUpdate = Lamp::Core::lampControl::getFormattedTimeAndDate();\n                        it->ArchivePath = Path;\n                        return Lamp::Core::FS::lampIO::saveModList(Ident().ShortHand,ModList,Games::getInstance().currentProfile);\n                    }\n                }\n            }\n\n            Core::Base::lampMod::Mod  * newArchive = new Core::Base::lampMod::Mod{Path, ArchiveModType, false};\n            newArchive->timeOfUpdate = Lamp::Core::lampControl::getFormattedTimeAndDate();\n            ModList.push_back(newArchive);\n            return Lamp::Core::FS::lampIO::saveModList(Ident().ShortHand,ModList,Games::getInstance().currentProfile);\n        }\n\n        lampReturn C77::ConfigMenu() {\n            ImGui::Separator();\n            Lamp::Core::lampControl::lampGameSettingsDisplayHelper(\"Cyberpunk 2077 Steam Directory\", keyInfo[\"installPath\"],\n                                                                   \"This is usually (steampath)/steamapps/common/Cyberpunk 2077/\",\n                                                                   \"installPath\").createImGuiMenu();\n            return false;\n        }\n\n        lampReturn C77::startDeployment() {\n            Lamp::Core::lampControl::getInstance().inDeployment = true;\n            if(KeyInfo()[\"installPath\"] == \"\" ) {\n                return Core::Base::lampLog::getInstance().pLog({0, \"Game Configuration not set.\"}, Core::Base::lampLog::warningLevel::WARNING, true, Core::Base::lampLog::LMP_NOCONFIG);\n            }\n\n            Core::Base::LampSequencer::add(\"BG3 Deployment Queue\", [this]() -> lampReturn {\n                Lamp::Core::lampControl::getInstance().deploymentStageTitle = \"Preparing\";\n                auto result = preCleanUp();\n                if(!result) Core::Base::lampLog::getInstance().log(\"Pre cleanup has failed.\", Core::Base::lampLog::warningLevel::ERROR, true, Core::Base::lampLog::LMP_CLEANUPFAILED);\n                return result;\n            });\n\n            Core::Base::LampSequencer::add(\"BG3 Deployment Queue\", [this]() -> lampReturn {\n                Lamp::Core::lampControl::getInstance().deploymentStageTitle = \"Pre Deployment\";\n                auto result = preDeployment();\n                if(!result) Core::Base::lampLog::getInstance().log(\"Pre Deployment has failed.\", Core::Base::lampLog::warningLevel::ERROR, true, Core::Base::lampLog::LMP_PREDEPLOYFAILED);\n                return result;\n            });\n\n            Core::Base::LampSequencer::add(\"BG3 Deployment Queue\", [this]() -> lampReturn {\n                Lamp::Core::lampControl::getInstance().deploymentStageTitle = \"Deploying\";\n                auto result = deployment();\n                if(!result) Core::Base::lampLog::getInstance().log(\"Deployment has failed.\", Core::Base::lampLog::warningLevel::ERROR, true, Core::Base::lampLog::LMP_DEOPLYMENTFAILED);\n                return result;\n            });\n\n\n            Core::Base::LampSequencer::run(\"BG3 Deployment Queue\");\n            Lamp::Core::lampControl::getInstance().inDeployment = false;\n            return false;\n        }\n\n        lampReturn C77::preCleanUp() {\n            std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath + Ident().ReadableName;\n            Lamp::Core::lampControl::getInstance().deplopmentTracker = {0,2};\n\n            std::filesystem::path dir(workingDir);\n            Core::Base::lampLog::getInstance().log(\"Cleaning Working Directory : \"+workingDir, Core::Base::lampLog::warningLevel::LOG);\n            try {\n                for (const auto &entry: std::filesystem::directory_iterator(dir)) {\n                    std::filesystem::remove_all(entry.path());\n                }\n            }catch(std::exception e){\n                return {-1, \"Cannot clean working directories\"};\n            }\n            Lamp::Core::lampControl::getInstance().deplopmentTracker.first = 1;\n\n\n            Core::Base::lampLog::getInstance().log(\"Creating Working Directories\", Core::Base::lampLog::warningLevel::LOG);\n            try {\n                std::filesystem::create_directories(workingDir + \"/Cyberpunk 2777/archive/pc/mod/\");\n            }catch(std::exception e){\n                return {-1, \"Unable to create working directories.\"};\n            }\n            Lamp::Core::lampControl::getInstance().deplopmentTracker.first = 2;\n            return {1 , \"\"};\n        }\n\n        lampReturn C77::preDeployment() {\n            Lamp::Core::lampControl::getInstance().deplopmentTracker = {0,0};\n            Core::Base::lampLog::getInstance().log(\"Extracting Archives\", Core::Base::lampLog::warningLevel::LOG);\n            auto lambdaFunction = [](const Core::Base::lampMod::Mod* item) {\n                if(item->enabled && item->modType != MOD_SEPARATOR) {\n                    Lamp::Core::lampControl::getInstance().deplopmentTracker.second++;\n                    if(Lamp::Core::FS::lampExtract::extract(item)) {\n\n                        std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath + Lamp::Games::getInstance().currentGame->Ident().ReadableName;\n                        std::filesystem::path dirit(workingDir + \"/ext/\" + std::filesystem::path(item->ArchivePath).filename().stem().string());\n\n                        std::string x = item->ArchivePath;\n                        Lamp::Core::FS::lampExtract::moveModSpecificFolder(item,\"archive\",\"Cyberpunk 2777/archive\");\n                        Lamp::Core::FS::lampExtract::moveModSpecificFolder(item,\"red4ext\",\"Cyberpunk 2777/red4ext\");\n                        Lamp::Core::FS::lampExtract::moveModSpecificFolder(item,\"r6\",\"Cyberpunk 2777/r6\");\n                        Lamp::Core::FS::lampExtract::moveModSpecificFolder(item,\"bin\",\"Cyberpunk 2777/bin\");\n                        Lamp::Core::FS::lampExtract::moveModSpecificFolder(item,\"engine\",\"Cyberpunk 2777/engine\");\n                        Lamp::Core::FS::lampExtract::moveModSpecificFolder(item,\"REDMod\",\"Cyberpunk 2777/REDMod\");\n                        Lamp::Core::FS::lampExtract::moveModSpecificFolder(item,\"mods\",\"Cyberpunk 2777/mods\");\n\n                        Lamp::Core::lampControl::getInstance().deplopmentTracker.first++;\n                    }\n                }\n            };\n            lampExecutor executor(lambdaFunction);\n            executor.execute(ModList, false);\n            return {1,\"TESTER\"};\n        }\n\n        lampReturn C77::deployment() {\n            std::string workingDir = Lamp::Core::lampConfig::getInstance().DeploymentDataPath + Ident().ReadableName;\n            Lamp::Core::lampControl::getInstance().deplopmentTracker = {0,1};\n\n                Lamp::Core::FS::lampTrack::handleFileDescriptor A{\n                        Lamp::Core::FS::lampTrack::handleFileDescriptor::operation::copyFolder,\n                        Lamp::Core::FS::lampTrack::handleFileDescriptor::mode::direct,\n                        workingDir+\"/Cyberpunk 2777\",\n                        keyInfo[\"installPath\"],\n                        \"\",\n                        Ident().ReadableName\n                };\n                Lamp::Core::FS::lampTrack::handleFile(A);\n                Lamp::Core::lampControl::getInstance().deplopmentTracker.first = 1;\n            return Lamp::Game::lampReturn();\n\n        }\n\n        lampReturn C77::postDeploymentTasks() {\n            return Lamp::Game::lampReturn();\n        }\n\n        void C77::listArchives() {\n            Lamp::Core::lampControl::lampArchiveDisplayHelper(\n                    std::list<std::string>{},\n                    ModList,\n                    std::list<std::pair<std::string, bool *>>{}\n            ).createImguiMenu();\n        }\n\n\n    } // Lampray\n} // Game"
  },
  {
    "path": "game-data/C77/C77.h",
    "content": "//\n// Created by charles on 14/10/23.\n//\n\n#ifndef LAMP_C77_H\n#define LAMP_C77_H\n\n#include \"../gameControl.h\"\n#include \"../../Lampray/Filesystem/lampFS.h\"\n\n\nnamespace Lamp::Game {\n\n        class C77 : public gameControl  {\n\n             lampReturn registerArchive(lampString Path, int ArchiveModType = -1) override;\n\n             lampReturn ConfigMenu() override;\n\n             lampReturn startDeployment() override;\n\n             lampReturn preCleanUp() override;\n\n             lampReturn preDeployment() override;\n\n             lampReturn deployment() override;\n\n             lampReturn postDeploymentTasks() override;\n\n             void listArchives() override;\n\n\n            std::vector<Core::Base::lampMod::Mod *> ModList {};\n\n            std::map<std::string,std::string>& KeyInfo() override { return keyInfo; };\n\n            Core::Base::lampTypes::lampIdent Ident() override { return {\"Cyberpunk 2077\",\"C77\"}; };\n\n            std::vector<Core::Base::lampMod::Mod *>& getModList() override{ return ModList; };\n\n            void launch() override {\n                for (const auto& pair : keyInfo) {\n                    const std::string& key = pair.first;\n                    keyInfo[key] = (std::string) Lamp::Core::FS::lampIO::loadKeyData(key,Ident().ShortHand).returnReason;\n                    if(key == \"ProfileList\"){\n                        if(pair.second == \"\" || pair.second == \"Default\") {\n                            keyInfo[key] = \"Default\";\n                            Lamp::Core::FS::lampIO::saveKeyData(key, keyInfo[key], Ident().ShortHand);\n                        }\n                    }\n                    if(key == \"CurrentProfile\"){\n                        if(pair.second == \"\" || pair.second == \"Default\") {\n                            keyInfo[key] = \"Default\";\n                            Lamp::Core::FS::lampIO::saveKeyData(key, keyInfo[key], Ident().ShortHand);\n                        }\n                    }\n                }\n                ModList = Lamp::Core::FS::lampIO::loadModList(Ident().ShortHand, keyInfo[\"CurrentProfile\"]);\n            }\n\n            void unmount() override{\n\n            }\n\n            int SeparatorModType(){\n                return MOD_SEPARATOR;\n            }\n\n            std::vector<std::pair<int, std::string> >& getModTypes() override {\n                return ModTypeList;\n            }\n\n            std::map<int, std::string>& getModTypesMap() override{\n                return ModTypeMap;\n            }\n\n            bool installPathSet() override{\n                if(this->KeyInfo()[\"installPath\"] == \"\"){\n                    return false;\n                }\n                return true;\n            }\n\n        private:\n\n            enum ModType{\n                C77_MOD,\n                MOD_SEPARATOR = 999\n            };\n\n            // use a vector to keep things organized, this allows us to output mod types in the order we define\n            std::vector<std::pair<int, std::string> > ModTypeList{\n                { C77_MOD, \"Mod\" },\n                { MOD_SEPARATOR, \"Separator\" }\n            };\n            // we will load the mod type vector above into this so we can get display values by the mod type value\n            std::map<int, std::string> ModTypeMap = initModTypesMap();\n\n            std::map<std::string,std::string> keyInfo{\n                    {\"installPath\",\"\"},\n                    {\"ProfileList\",\"Default\"},\n                    {\"CurrentProfile\", \"Default\"}\n            };\n        };\n\n    } // Lampray\n\n#endif //LAMP_C77_H\n"
  },
  {
    "path": "game-data/LHL/LHL.cpp",
    "content": "//\n// Created by charles on 13/12/23.\n//\n\n#include \"LHL.h\"\n\nnamespace Lamp {\n    namespace Game {\n    } // Game\n} // Lampray"
  },
  {
    "path": "game-data/LHL/LHL.h",
    "content": "//\n// Created by charles on 13/12/23.\n//\n\n#ifndef LAMP_LHL_H\n#define LAMP_LHL_H\n\nnamespace Lamp {\n    namespace Game {\n\n        class LHL {\n\n        };\n\n    } // Game\n} // Lampray\n\n#endif //LAMP_LHL_H\n"
  },
  {
    "path": "game-data/gameControl.h",
    "content": "//\n// Created by charles on 11/09/23.\n//\n\n#ifndef LAMP_GAMECONTROL_H\n#define LAMP_GAMECONTROL_H\n\n#include <string>\n#include <list>\n#include <map>\n#include \"../Lampray/Base/lampBase.h\"\n\n\nnamespace Lamp::Game {\n    typedef Core::Base::lampTypes::lampString lampString;\n    typedef Core::Base::lampTypes::lampHexAlpha lampHex;\n    typedef Core::Base::lampTypes::lampReturn lampReturn;\n    typedef Core::Base::lampExecutor lampExecutor;\n\n    /**\n * @brief A base class for controlling game-related functionality.\n *\n * This class defines a set of virtual functions that need to be implemented by derived classes\n * to manage various aspects of a game.\n */\n    class gameControl {\n    public:\n\n        /**\n         * @brief Registers an archive with the game control.\n         * This is called during a file drop event onto lamp.\n         * @param Path The path to the archive to be registered.\n         * @param ArchiveModType integer mod type to use for the added archive\n         * @return A lampReturn object indicating the success or failure of the operation.\n         */\n        virtual lampReturn registerArchive(lampString Path, int ArchiveModType = -1) = 0;\n\n        /**\n         * @brief Displays a configuration menu for the game.\n         * See Lampray::Game::BG3 for usage and Lampray::Core::lampControl::lampGameSettingsDisplayHelper for creating config menu items.\n         * @return A lampReturn object indicating the success or failure of the operation.\n         */\n        virtual lampReturn ConfigMenu() = 0;\n\n        /**\n         * @brief Starts the deployment process for the game.\n         * Called externally by Lampray::Core to start deployment.\n         * @return A lampReturn object indicating the success or failure of the deployment.\n         */\n        virtual lampReturn startDeployment() = 0;\n\n        /**\n         * @brief Performs pre-cleanup tasks before preDeployment.\n         * Called internally, this is the step for cleaning up game files and internal folders.\n         * @return A lampReturn object indicating the success or failure of the pre-cleanup.\n         */\n        virtual lampReturn preCleanUp() = 0;\n\n        /**\n         * @brief Performs pre-deployment tasks before Deployment.\n         * Called internally, this is the step for extracting archives and moving files to relevant positions before linking.\n         * @return A lampReturn object indicating the success or failure of the pre-deployment.\n         */\n        virtual lampReturn preDeployment() = 0;\n\n        /**\n         * @brief Deploys mod files.\n         * Called internally, this is the step for linking the local mod setup to the actual game.\n         * @return A lampReturn object indicating the success or failure of the deployment.\n         */\n        virtual lampReturn deployment() = 0;\n\n        /**\n         * @brief Performs post-deployment tasks after deployment.\n         * Called internally, this would be used for calls to external programs such as fnis or Bodyslide.\n         * @return A lampReturn object indicating the success or failure of the post-deployment tasks.\n         */\n        virtual lampReturn postDeploymentTasks() = 0;\n\n        /**\n         * @brief Lists the registered archives in the game control.\n         * Called Externally, this should be used with Lampray::Core::lampControl::lampArchiveDisplayHelper unless a custom solution is desired.\n         */\n        virtual void listArchives() = 0;\n\n        /**\n         * @brief Retrieves key information associated with the game control.\n         * Returns an internal map that must contain and is also used for any relevant string information.\n         * {\"ProfileList\",\"Default\"},\n         * {\"CurrentProfile\", \"Default\"}\n         * @return A reference to a map containing key-value pairs of information.\n         */\n        virtual std::map<std::string, std::string>& KeyInfo() = 0;\n\n        /**\n         * @brief Retrieves the identification information for the game control.\n         * This is the games identifier something that must be set, see Core::Base::lampTypes::lampIdent.\n         * @return A lampIdent object representing the identification information.\n         */\n        virtual Core::Base::lampTypes::lampIdent Ident() = 0;\n\n        /**\n         * @brief Retrieves a list of game mods associated with the game control.\n         * Returns the current modlist. This is used heavily within lamp.\n         * @return A reference to a vector of lampMod pointers representing the game mods.\n         */\n        virtual std::vector<Core::Base::lampMod::Mod *>& getModList() = 0;\n\n        /**\n         * @brief Runs any setup tasks required for the game.\n         */\n        virtual void launch() = 0;\n\n        virtual void unmount() = 0;\n\n        virtual int SeparatorModType(){\n            return MOD_SEPARATOR;\n        }\n\n        virtual std::vector<std::pair<int, std::string> >& getModTypes() {\n            return ModTypeList;\n        }\n\n        virtual std::map<int, std::string>& getModTypesMap(){\n            return ModTypeMap;\n        }\n\n        std::map<int, std::string> initModTypesMap() {\n            std::map<int, std::string> returnModTypes = {};\n            for(auto it = getModTypes().begin(); it != getModTypes().end(); ++it){\n                auto key = (*it).first;\n                auto value = (*it).second;\n                returnModTypes.insert({key, value});\n            }\n            return returnModTypes;\n        }\n\n        /**\n         * @brief Checks if all necessary paths are configured for the given game.\n         * @return true if all necessary paths are set for this game, otherwise false.\n         */\n        virtual bool installPathSet(){\n            return true;\n        }\n\n    protected:\n        /**\n         * @brief Protected constructor for the game control class.\n         *\n         * This constructor is protected to ensure that only derived classes can be instantiated.\n         */\n        gameControl(){}\n    private:\n\n        // use a vector to keep things organized, this allows us to output mod types in the order we define\n        std::vector<std::pair<int, std::string> > ModTypeList{\n            { GENERIC_MOD, \"Mod\" },\n            { MOD_SEPARATOR, \"Separator\" }\n        };\n        // we will load the mod type vector above into this so we can get display values by the mod type value\n        std::map<int, std::string> ModTypeMap = {};\n\n        enum ModType{\n            GENERIC_MOD = 0,\n            MOD_SEPARATOR = 999\n        };\n    };\n} // Lampray\n\n#endif //LAMP_GAMECONTROL_H\n"
  },
  {
    "path": "main.cpp",
    "content": "#include \"Lampray/Control/lampConfig.h\"\n#include \"third-party/imgui/imgui.h\"\n#include \"third-party/imgui/imgui_impl_sdl2.h\"\n#include \"third-party/imgui/imgui_impl_sdlrenderer2.h\"\n#include \"Lampray/Lang/lampLang.h\"\n#include \"Lampray/Control/lampControl.h\"\n#include \"Lampray/Menu/lampMenu.h\"\n#include \"Lampray/Menu/lampCustomise.h\"\n#include <stdio.h>\n#include \"SDL2/SDL.h\"\n\n#include \"Lampray/Control/lampNotification.h\"\n\n#if !SDL_VERSION_ATLEAST(2,0,17)\n#error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function\n#endif\n\n// Main code\nint main(int, char**)\n{\n    // Setup SDL\n    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0)\n    {\n        printf(\"Error: %s\\n\", SDL_GetError());\n        return -1;\n    }\n\n    // From 2.0.18: Enable native IME.\n#ifdef SDL_HINT_IME_SHOW_UI\n    SDL_SetHint(SDL_HINT_IME_SHOW_UI, \"1\");\n#endif\n\n    SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);\n    SDL_Window* window = SDL_CreateWindow(\"Lampray\", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags);\n    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);\n    if (renderer == nullptr)\n    {\n        SDL_Log(\"Error creating SDL_Renderer!\");\n        return 0;\n    }\n\n    IMGUI_CHECKVERSION();\n    ImGui::CreateContext();\n    ImGuiIO& io = ImGui::GetIO(); (void)io;\n    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls\n    io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls\n\n    ImGui_ImplSDL2_InitForSDLRenderer(window, renderer);\n    ImGui_ImplSDLRenderer2_Init(renderer);\n\n    auto fontFolder = Lamp::Core::lampConfig::getInstance().baseDataPath + \"Fonts/\";\n\n    // Check if the \"Font\" folder exists\n    if (std::filesystem::is_directory(fontFolder)) {\n        for (const auto& entry : std::filesystem::directory_iterator(fontFolder)) {\n            if (entry.is_regular_file() && entry.path().extension() == \".ttf\") {\n                io.Fonts->AddFontFromFileTTF(entry.path().string().c_str(), 16.0f);\n                break;\n            }\n        }\n    }\n\n    ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);\n\n    std::string preferredLanguage = Lamp::Core::FS::lampIO::loadKeyData(\"LanguagePath\", \"LAMP CONFIG\");\n    Lamp::Core::lampLang::getInstance().CurrentLanguage = Lamp::Core::lampLang::LanguageContainer(); \n    \n    auto languageLoaded = Lamp::Core::lampLang::getInstance()\n                          .CurrentLanguage.build(preferredLanguage);\n    Lamp::Core::Base::lampLog::getInstance().log(languageLoaded.returnReason);\n    if(!languageLoaded) {\n        auto path = Lamp::Core::lampLang::getInstance().createEnglishUK();\n        Lamp::Core::Base::lampLog::getInstance().log(\n          Lamp::Core::lampLang::getInstance()\n            .CurrentLanguage.build(path)\n              .returnReason);\n    }\n\n\n\n    Lamp::Core::lampControl::getInstance().Colour_SearchHighlight = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[13]);\n    ImGui::GetStyle().Colors[ImGuiCol_Text] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[0]);\n    ImGui::GetStyle().Colors[ImGuiCol_WindowBg] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[1]);\n    ImGui::GetStyle().Colors[ImGuiCol_PopupBg] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[2]);\n    ImGui::GetStyle().Colors[ImGuiCol_FrameBg] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[3]);\n    ImGui::GetStyle().Colors[ImGuiCol_MenuBarBg] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[4]);\n    ImGui::GetStyle().Colors[ImGuiCol_Button] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[5]);\n    ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[6]);\n    ImGui::GetStyle().Colors[ImGuiCol_ButtonActive] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[7]);\n\n    ImGui::GetStyle().Colors[ImGuiCol_Header] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[8]);\n    ImGui::GetStyle().Colors[ImGuiCol_HeaderHovered] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[9]);\n    ImGui::GetStyle().Colors[ImGuiCol_HeaderActive] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[10]);\n\tImGui::GetStyle().Colors[ImGuiCol_Separator] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[11]);\n    ImGui::GetStyle().Colors[ImGuiCol_SeparatorHovered] = Lamp::Core::Base::lampTypes::lampHexAlpha(Lamp::Core::lampCustomise::getInstance().defaultColours[12]);\n\n    std::string PreviousGame = Lamp::Core::FS::lampIO::loadKeyData(\"PreviousGame\",\"LAMP CONFIG\");\n    if(PreviousGame != \"\") {\n        Lamp::Games::getInstance().currentGameInt = std::stoi(PreviousGame);\n        Lamp::Games::getInstance().currentGame = Lamp::Games::getInstance().gameList[Lamp::Games::getInstance().currentGameInt];\n    }\n\n    Lamp::Core::Base::lampLog::getInstance().log(\"Clearing log file.\");\n    const std::string filename = \"lamp.log\";\n    std::ofstream file(filename, std::ios::trunc);\n    if (!file) {\n        file.close();\n        Lamp::Core::Base::lampLog::getInstance().log(\"Couldn't clear the Log.\");\n    }\n    file.close();\n\n    Lamp::Core::Base::lampLog::getInstance().log(Lamp::Core::lampControl::getFormattedTimeAndDate()+\" | | Battle Control Online, Welcome Back Commander.\", Lamp::Core::Base::lampLog::LOG);\n    Lamp::Core::Base::lampLog::getInstance().log(\"Lampray version: \" + Lamp::Core::FS::lampUpdate::getInstance().versionNumber);\n\n    Lamp::Games::getInstance();\n\n    std::string loadedCheckUpdates = Lamp::Core::FS::lampIO::loadKeyData(\"Check_Updates_Startup\", \"LAMP CONFIG\");\n    bool checkForUpdates = true;\n    if(loadedCheckUpdates == \"0\" || loadedCheckUpdates == \"false\"){\n        checkForUpdates = false;\n    }\n    if(checkForUpdates){\n        Lamp::Core::FS::lampUpdate::getInstance().checkForUpdates();\n    }\n    Lamp::Core::lampConfig::getInstance().lampFlags[\"showIntroMenu\"]=(std::string)Lamp::Core::FS::lampIO::loadKeyData(\"showIntroMenu\",\"LAMP CONFIG\").returnReason;\n    Lamp::Core::lampConfig::getInstance().bit7zLibraryLocation = (std::string)Lamp::Core::FS::lampIO::loadKeyData(\"bit7zLibraryLocation\",\"LAMP CONFIG\").returnReason;\n    bool found7z = Lamp::Core::lampConfig::getInstance().init();\n    if(!found7z){\n        Lamp::Core::lampNotification::getInstance().pushErrorNotification(Lamp::Core::lampLang::getInstance().LS(\"LAMPRAY_ERROR_7Z\"));\n    }\n    Lamp::Core::FS::lampIO::saveKeyData(\"bit7zLibraryLocation\", Lamp::Core::lampConfig::getInstance().bit7zLibraryLocation, \"LAMP CONFIG\");\n\n    Lamp::Core::lampMenu Menus;\n    // This is a very inefficent way of doing this.\n\n\n    Lamp::Games::getInstance().currentProfile = Lamp::Games::getInstance().currentGame->KeyInfo()[\"CurrentProfile\"];\n\n    // Try to load and set the global font scale. I am pretty sure this is not a good way of doing this...\n    std::string loadedFontScale = Lamp::Core::FS::lampIO::loadKeyData(\"Font_Scale\", \"LAMP CONFIG\");\n    if(loadedFontScale != \"\"){\n        io.FontGlobalScale = std::stof(loadedFontScale);\n    }\n\n    Lamp::Core::Base::lampLog::getInstance().log(\"Creating Directories\");\n    try {\n        std::filesystem::create_directories(Lamp::Core::lampConfig::getInstance().saveDataPath);\n        std::filesystem::create_directories(Lamp::Core::lampConfig::getInstance().archiveDataPath);\n        std::filesystem::create_directories(Lamp::Core::lampConfig::getInstance().ConfigDataPath);\n        std::filesystem::create_directories(Lamp::Core::lampConfig::getInstance().DeploymentDataPath);\n        std::filesystem::create_directories(Lamp::Core::lampConfig::getInstance().workingPaths);\n        for (Lamp::Game::gameControl *element: Lamp::Games::getInstance().gameList){\n            std::filesystem::create_directories(std::filesystem::path(Lamp::Core::lampConfig::getInstance().DeploymentDataPath + element->Ident().ReadableName));\n            std::filesystem::create_directories(std::filesystem::path(Lamp::Core::lampConfig::getInstance().archiveDataPath + element->Ident().ReadableName + \"/GameFiles\"));\n            std::filesystem::create_directories(Lamp::Core::lampConfig::getInstance().workingPaths + element->Ident().ReadableName);\n            std::filesystem::create_directories(Lamp::Core::lampConfig::getInstance().ConfigDataPath + element->Ident().ReadableName);\n        }\n        std::filesystem::create_directories(Lamp::Core::lampConfig::getInstance().workingPaths + Lamp::Games::getInstance().gameList[0]->Ident().ReadableName+\"/MODS/\");\n        std::filesystem::create_directories(Lamp::Core::lampConfig::getInstance().workingPaths + Lamp::Games::getInstance().gameList[0]->Ident().ReadableName+\"/STEAM/\");\n        std::filesystem::create_directories(Lamp::Core::lampConfig::getInstance().ConfigDataPath + Lamp::Games::getInstance().gameList[0]->Ident().ReadableName+\"/MODS/\");\n        std::filesystem::create_directories(Lamp::Core::lampConfig::getInstance().ConfigDataPath + Lamp::Games::getInstance().gameList[0]->Ident().ReadableName+\"/STEAM/\");\n    } catch (std::exception ex) {\n        Lamp::Core::Base::lampLog::getInstance().log(\"Could not create base directories\", Lamp::Core::Base::lampLog::ERROR,\n                                                     Lamp::Core::Base::lampLog::LMP_NODIRCREATION);\n    }\n\n    Lamp::Core::Base::lampLog::getInstance().log(\"Directories Created\");\n\n    Lamp::Core::lampCustomise::getInstance().getConfigColours();\n\n    bool done = false;\n    while (!done)\n    {\n        SDL_Event event;\n        while (SDL_PollEvent(&event))\n        {\n            ImGui_ImplSDL2_ProcessEvent(&event);\n            if (event.type == SDL_QUIT)\n                done = true;\n            if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))\n                done = true;\n\n            if (event.type == SDL_DROPFILE) {\n                // A file has been dropped\n                char* droppedFile = event.drop.file;\n                Lamp::Core::FS::lampIO::fileDrop(droppedFile);\n                SDL_free(droppedFile);\n            }\n        }\n\n\n        ImGui_ImplSDLRenderer2_NewFrame();\n        ImGui_ImplSDL2_NewFrame();\n        ImGui::NewFrame();\n\n        Menus.RunMenus();\n\n        ImGui::Render();\n        SDL_RenderSetScale(renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);\n        SDL_SetRenderDrawColor(renderer, (Uint8)(clear_color.x * 255), (Uint8)(clear_color.y * 255), (Uint8)(clear_color.z * 255), (Uint8)(clear_color.w * 255));\n        SDL_RenderClear(renderer);\n        ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());\n        SDL_RenderPresent(renderer);\n\n        if(Menus.userRequestedQuit){\n            done = true;\n        }\n    }\n\n    ImGui_ImplSDLRenderer2_Shutdown();\n    ImGui_ImplSDL2_Shutdown();\n    ImGui::DestroyContext();\n\n    SDL_DestroyRenderer(renderer);\n    SDL_DestroyWindow(window);\n    SDL_Quit();\n\n    return 0;\n}\n"
  },
  {
    "path": "setup.sh",
    "content": "#!/usr/bin/env bash\n\n#\n# Setup ASDF and Plugins\n#  ASDF_PLUGINS=(nodejs pnpm bats) \\\n#  ASDF_PLUGIN_URL_nodejs=https://someurl \\\n#  ./pkg/setup-tooling/setup.bash\n#\n\nset -e\n\n[ $(command -v git) ] || {\n  echo \"📛📦 Missing git\"\n  exit 1\n}\n[ $(command -v curl) ] || {\n  echo \"📛📦 Missing curl\"\n  exit 1\n}\n\n# Local vars\nASDF_VERSION=${ASDF_VERSION:-v0.8.1}\nASDF_HOME=$HOME/.asdf\nASDF_BIN=$ASDF_HOME/asdf.sh\n\n#\n# Helper to idempotently add strings to target files\n#\nappend_uniquely() {\n  if ! grep -q \"$2\" \"$1\"; then\n    echo \"====> ✍ Writing \\\"$2\\\" into \\\"$1\\\" \"\n    echo \"${2}\" >>$1\n  fi\n}\n\n\ncase \"${SHELL}\" in\n/bin/bash)\n  SHELL_PROFILE=~/.bashrc\n  SHELL_NAME=\"bash\"\n  ;;\n/bin/zsh)\n  SHELL_PROFILE=~/.zshrc\n  SHELL_NAME=\"zsh\"\n  ;;\nesac\n\necho \"=> 💁 [ASDF] install with plugins\"\n\nif [ ! -f \"$ASDF_BIN\" ]; then\n  echo \"===> ⤵️ ASDF not detected ... installing\"\n  git clone https://github.com/asdf-vm/asdf.git \"$ASDF_HOME\" --branch $ASDF_VERSION\n  [ ! command -v asdf ] &>/dev/null && {\n    echo \"====> ⚕️ adding to shell profile\"\n    append_uniquely \"$SHELL_PROFILE\" \". $ASDF_HOME/asdf.sh\"\n    append_uniquely \"$SHELL_PROFILE\" \". $ASDF_HOME/completions/asdf.bash\"\n  }\nfi\n\nsource \"$ASDF_BIN\"\n\nfor plugin in $(cut -d' ' -f1 ./.tool-versions)\ndo\n  echo \"\n==> 💁 [ASDF] Ensure ${plugin} plugin\"\n\n  if [ -d \"$ASDF_HOME/plugins/${plugin}\" ]; then\n    echo \"===> 📦 attempting upgrade\"\n    asdf plugin-update \"${plugin}\"\n  else\n    echo \"===> 🌐 installing\"\n    plugin_url_var=ASDF_PLUGIN_URL_${plugin//-/_}\n    plugin_url=\"${!plugin_url_var}\"\n\n    if [ ${!plugin_url_var+x} ]; then\n      echo \"====> 💁 [${plugin}] installed from ${plugin_url}\"\n    fi\n\n    asdf plugin-add \"${plugin}\" \"${plugin_url}\"\n  fi\ndone\n\n\necho \"==> 💁 [ASDF] install tools\"\nasdf install\n\necho \"==> 💁 [ASDF] reshim globals\"\nasdf reshim\n\necho \"==> 💁 [ASDF] Done ✅\"\n"
  },
  {
    "path": "third-party/bit7z/BUILD.txt",
    "content": "v4.0.0-rc gcc9.4.0_x64\n"
  },
  {
    "path": "third-party/bit7z/LICENSE",
    "content": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n    means each individual or legal entity that creates, contributes to\n    the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n    means the combination of the Contributions of others (if any) used\n    by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n    means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n    means Source Code Form to which the initial Contributor has attached\n    the notice in Exhibit A, the Executable Form of such Source Code\n    Form, and Modifications of such Source Code Form, in each case\n    including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n    means\n\n    (a) that the initial Contributor has attached the notice described\n        in Exhibit B to the Covered Software; or\n\n    (b) that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the\n        terms of a Secondary License.\n\n1.6. \"Executable Form\"\n    means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n    means a work that combines Covered Software with other material, in \n    a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n    means this document.\n\n1.9. \"Licensable\"\n    means having the right to grant, to the maximum extent possible,\n    whether at the time of the initial grant or subsequently, any and\n    all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n    means any of the following:\n\n    (a) any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered\n        Software; or\n\n    (b) any new file in Source Code Form that contains any Covered\n        Software.\n\n1.11. \"Patent Claims\" of a Contributor\n    means any patent claim(s), including without limitation, method,\n    process, and apparatus claims, in any patent Licensable by such\n    Contributor that would be infringed, but for the grant of the\n    License, by the making, using, selling, offering for sale, having\n    made, import, or transfer of either its Contributions or its\n    Contributor Version.\n\n1.12. \"Secondary License\"\n    means either the GNU General Public License, Version 2.0, the GNU\n    Lesser General Public License, Version 2.1, the GNU Affero General\n    Public License, Version 3.0, or any later versions of those\n    licenses.\n\n1.13. \"Source Code Form\"\n    means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n    means an individual or a legal entity exercising rights under this\n    License. For legal entities, \"You\" includes any entity that\n    controls, is controlled by, or is under common control with You. For\n    purposes of this definition, \"control\" means (a) the power, direct\n    or indirect, to cause the direction or management of such entity,\n    whether by contract or otherwise, or (b) ownership of more than\n    fifty percent (50%) of the outstanding shares or beneficial\n    ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n    Licensable by such Contributor to use, reproduce, make available,\n    modify, display, perform, distribute, and otherwise exploit its\n    Contributions, either on an unmodified basis, with Modifications, or\n    as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n    for sale, have made, import, and otherwise transfer either its\n    Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n    or\n\n(b) for infringements caused by: (i) Your and any other third party's\n    modifications of Covered Software, or (ii) the combination of its\n    Contributions with other software (except as part of its Contributor\n    Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n    its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n    Form, as described in Section 3.1, and You must inform recipients of\n    the Executable Form how they can obtain a copy of such Source Code\n    Form by reasonable means in a timely manner, at a charge no more\n    than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n    License, or sublicense it under different terms, provided that the\n    license for the Executable Form does not attempt to limit or alter\n    the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n*                                                                      *\n*  6. Disclaimer of Warranty                                           *\n*  -------------------------                                           *\n*                                                                      *\n*  Covered Software is provided under this License on an \"as is\"       *\n*  basis, without warranty of any kind, either expressed, implied, or  *\n*  statutory, including, without limitation, warranties that the       *\n*  Covered Software is free of defects, merchantable, fit for a        *\n*  particular purpose or non-infringing. The entire risk as to the     *\n*  quality and performance of the Covered Software is with You.        *\n*  Should any Covered Software prove defective in any respect, You     *\n*  (not any Contributor) assume the cost of any necessary servicing,   *\n*  repair, or correction. This disclaimer of warranty constitutes an   *\n*  essential part of this License. No use of any Covered Software is   *\n*  authorized under this License except under this disclaimer.         *\n*                                                                      *\n************************************************************************\n\n************************************************************************\n*                                                                      *\n*  7. Limitation of Liability                                          *\n*  --------------------------                                          *\n*                                                                      *\n*  Under no circumstances and under no legal theory, whether tort      *\n*  (including negligence), contract, or otherwise, shall any           *\n*  Contributor, or anyone who distributes Covered Software as          *\n*  permitted above, be liable to You for any direct, indirect,         *\n*  special, incidental, or consequential damages of any character      *\n*  including, without limitation, damages for lost profits, loss of    *\n*  goodwill, work stoppage, computer failure or malfunction, or any    *\n*  and all other commercial damages or losses, even if such party      *\n*  shall have been informed of the possibility of such damages. This   *\n*  limitation of liability shall not apply to liability for death or   *\n*  personal injury resulting from such party's negligence to the       *\n*  extent applicable law prohibits such limitation. Some               *\n*  jurisdictions do not allow the exclusion or limitation of           *\n*  incidental or consequential damages, so this exclusion and          *\n*  limitation may not apply to You.                                    *\n*                                                                      *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n  This Source Code Form is subject to the terms of the Mozilla Public\n  License, v. 2.0. If a copy of the MPL was not distributed with this\n  file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n  This Source Code Form is \"Incompatible With Secondary Licenses\", as\n  defined by the Mozilla Public License, v. 2.0.\n"
  },
  {
    "path": "third-party/bit7z/README.md",
    "content": "<h1 align=\"center\">bit7z</h1>\n\n<h3 align=\"center\">A C++ static library offering a clean and simple interface to the 7-zip shared libraries</h3>\n\n<!-- navbar -->\n<p align=\"center\">\n  <a href=\"#-supported-features\" title=\"List of Features Supported by the Library\">Supported Features</a> •\n  <a href=\"#-getting-started-library-usage\" title=\"Basic Source Code Examples\">Getting Started</a> •\n  <a href=\"#-download\" title=\"Download Pre-compiled Packages\">Download</a> •\n  <a href=\"#-requirements\" title=\"Usage Requirements\">Requirements</a> •\n  <a href=\"#%EF%B8%8F-building-and-using-bit7z\" title=\"Building the Library\">Building & Using</a> •\n  <a href=\"#%EF%B8%8F-donate\" title=\"Support the Project\">Donate</a> •\n  <a href=\"#-license\" title=\"Project License\">License</a>\n</p>\n<!-- navbar -->\n\n<div align=\"center\">\n  <a href=\"https://github.com/rikyoz/bit7z/releases\" title=\"Latest Stable GitHub Release\"><img src=\"https://img.shields.io/github/release/rikyoz/bit7z/all.svg?style=flat&logo=github&logoColor=white&colorB=blue&label=\" alt=\"GitHub release\"></a>&thinsp;<img src=\"https://img.shields.io/badge/-C++14/17-3F63B3.svg?style=flat&logo=C%2B%2B&logoColor=white\" alt=\"C++14/17\" title=\"C++ Standards Used: C++14/17\">&thinsp;<img src=\"https://img.shields.io/badge/-Windows-6E46A2.svg?style=flat&logo=windows-11&logoColor=white\" alt=\"Windows\" title=\"Supported Platform: Windows\">&thinsp;<img src=\"https://img.shields.io/badge/-Linux-9C2A91.svg?style=flat&logo=linux&logoColor=white\" alt=\"Linux\" title=\"Supported Platform: Linux\">&thinsp;<img src=\"https://img.shields.io/badge/-macOS-red.svg?style=flat&logo=apple&logoColor=white\" alt=\"macOS\" title=\"Supported Platform: macOS\">&thinsp;<img src=\"https://img.shields.io/badge/-x86%20&middot;%20x64-orange.svg?style=flat&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHZpZXdCb3g9IjAgMCA5NDIgOTQyIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNTc5LjEgODk0YTQ4IDQ4IDAgMCAwIDk2IDB2LTc3LjVoLTk1LjlWODk0aC0uMXpNNTc5LjEgNDh2NzcuNUg2NzVWNDhhNDggNDggMCAwIDAtOTUuOSAwek00MjMgNDh2NzcuNWg5NlY0OGE0OCA0OCAwIDAgMC05NiAwek00MjMgODk0YTQ4IDQ4IDAgMCAwIDk2IDB2LTc3LjVoLTk2Vjg5NHpNMjY3IDQ4djc3LjVoOTUuOVY0OGE0OCA0OCAwIDAgMC05NS45IDB6TTI2NyA4OTRhNDggNDggMCAwIDAgOTYgMHYtNzcuNWgtOTZWODk0ek0wIDYyN2E0OCA0OCAwIDAgMCA0OCA0OGg3Ny41di05NS45SDQ4QTQ4IDQ4IDAgMCAwIDAgNjI3ek04OTQgNTc5LjFoLTc3LjVWNjc1SDg5NGE0OCA0OCAwIDAgMCAwLTk1Ljl6TTAgNDcxYTQ4IDQ4IDAgMCAwIDQ4IDQ4aDc3LjV2LTk2SDQ4YTQ4IDQ4IDAgMCAwLTQ4IDQ4ek04OTQgNDIzaC03Ny41djk2SDg5NGE0OCA0OCAwIDAgMCAwLTk2ek0wIDMxNWE0OCA0OCAwIDAgMCA0OCA0OGg3Ny41di05Nkg0OGE0OCA0OCAwIDAgMC00OCA0OHpNODk0IDI2N2gtNzcuNXY5NS45SDg5NGE0OCA0OCAwIDAgMCAwLTk1Ljl6TTE3MS42IDcyMC40YTUwIDUwIDAgMCAwIDUwIDUwaDQ5OC44YTUwIDUwIDAgMCAwIDUwLTUwVjIyMS42YTUwIDUwIDAgMCAwLTUwLTUwSDIyMS42YTUwIDUwIDAgMCAwLTUwIDUwdjQ5OC44eiIvPjwvc3ZnPg==\" alt=\"x86, x64\" title=\"Supported CPU Architectures: x86, x64\">&thinsp;<a href=\"#%EF%B8%8F-donate\" title=\"Donate\"><img src=\"https://img.shields.io/badge/-donate-yellow.svg?style=flat&logo=paypal&logoColor=white\" alt=\"donate\"></a>&thinsp;<a href=\"https://github.com/rikyoz/bit7z/wiki\" title=\"Project Documentation\"><img src=\"https://img.shields.io/badge/-docs-green.svg?style=flat&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGRhdGEtbmFtZT0iTGF5ZXIgMSIgdmlld0JveD0iMCAwIDEwNS4zIDEyMi45Ij48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiNmZmYiIGQ9Ik0xNy41IDBIMTAydjk0LjJjLS4xIDIuNy0zLjUgMi43LTcuMiAyLjZIMTYuM2E5LjIgOS4yIDAgMCAwIDAgMTguNEg5OHYtOS44aDcuMlYxMThhNC4yIDQuMiAwIDAgMS00LjEgNC4xSDE2LjZDNy41IDEyNS41IDAgMTE4IDAgMTA4LjhWMTcuNUExNy42IDE3LjYgMCAwIDEgMTcuNSAwWm0tMS4zIDEwOGg3NS4yYTEuNCAxLjQgMCAwIDEgMS40IDEuM3YuOGExLjQgMS40IDAgMCAxLTEuNCAxLjRIMTYuMmExLjQgMS40IDAgMCAxLTEuMy0xLjR2LS44YTEuNCAxLjQgMCAwIDEgMS4zLTEuNFptMC03LjJoNzUuMmExLjQgMS40IDAgMCAxIDEuNCAxLjR2LjhhMS40IDEuNCAwIDAgMS0xLjQgMS40SDE2LjJBMS40IDEuNCAwIDAgMSAxNSAxMDN2LS44YTEuNCAxLjQgMCAwIDEgMS4zLTEuNFoiLz48L3N2Zz4=\" alt=\"docs\"></a>&thinsp;<a href=\"https://ci.appveyor.com/project/rikyoz/bit7z\" title=\"AppVeyor CI Build Status\"><img src=\"https://img.shields.io/appveyor/ci/rikyoz/bit7z.svg?style=flat&logo=appveyor&logoColor=white&label=\" alt=\"Build status\"></a>\n  <br>\n  <img src=\"https://img.shields.io/badge/MSVC%202015+-flag.svg?color=555555&style=flat&logo=visual%20studio&logoColor=white\" alt=\"MSVC 2015+\" title=\"Supported Windows Compiler: MSVC 2015 or later\">&thinsp;<img src=\"https://img.shields.io/badge/MinGW%206.4+%20-flag.svg?color=555555&style=flat&logo=windows&logoColor=white\" alt=\"MinGW 6.4+\" title=\"Supported Windows Compiler: MinGW 6.4 or later\">&thinsp;<img src=\"https://img.shields.io/badge/GCC%204.9+-flag.svg?color=555555&style=flat&logo=gnu&logoColor=white\" alt=\"GCC 4.9+\" title=\"Supported Unix Compiler: GCC 4.9 or later\">&thinsp;<img src=\"https://img.shields.io/badge/Clang%203.5+-flag.svg?color=555555&style=flat&logo=llvm&logoColor=white\" alt=\"Clang 3.5+\" title=\"Supported Unix Compiler: Clang 3.5 or later\">&thinsp;<img alt=\"CodeFactor Grade\" src=\"https://img.shields.io/codefactor/grade/github/rikyoz/bit7z?label=Code%20Quality&logo=codefactor&logoColor=white\">&thinsp;<a href=\"https://github.com/rikyoz/bit7z/blob/master/LICENSE\" title=\"Project License: MPLv2\"><img src=\"https://img.shields.io/badge/-MPL--2.0-lightgrey.svg?style=flat&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGwtcnVsZT0iZXZlbm9kZCIgaW1hZ2UtcmVuZGVyaW5nPSJvcHRpbWl6ZVF1YWxpdHkiIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB2aWV3Qm94PSIwIDAgNTEyIDQzMCIgeG1sbnM6dj0iaHR0cHM6Ly92ZWN0YS5pby9uYW5vIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMzUgMGg0NDJjMTkgMCAzNSAxNiAzNSAzNXYzMDBjMCAxOS0xNiAzNS0zNSAzNWgtNjlsLTExLTIyaDgyYzUgMCAxMC00IDEwLTEwVjk3SDI1djI0MWMwIDYgNCAxMCA5IDEwaDgxbC05IDE4LTIgNEgzNWMtMTkgMC0zNS0xNi0zNS0zNVYzNUMwIDE2IDE2IDAgMzUgMHptMjI5IDE1MWM1IDIgOSA1IDEzIDkgOSA3IDEwIDkgMjMgOWwxNCAyYzggMyAxMyAxMCAxNSAxOCAyIDcgMSAxNCA0IDIxIDIgNyA3IDEyIDExIDE4IDMgNSA0IDkgNSAxM2EyNSAyNSAwIDAgMS03IDI0bC05IDlhMjYgMjYgMCAwIDAtMiAxMWMwIDUgMCAxMC0zIDE3YTMyIDMyIDAgMCAxLTIwIDE2Yy02IDItMTQgMC0xOCAyaC0xYy04IDMtMTUgMTItMjMgMTVhMzEgMzEgMCAwIDEtMTAgMmwtOS0yaC0xYy00LTEtOC00LTEyLTgtMy0zLTctNi0xMi04aC03bC0xMS0xYy01LTEtOC0zLTEyLTZhMzMgMzMgMCAwIDEtOC0xMGMtMy03LTMtMTItMy0xNyAwLTkgMC0xMS03LTE2LTgtOC0xNC0xNi0xMi0yOCAyLTYgNS0xMiAxMS0yMGEzNyAzNyAwIDAgMCA2LTExYzMtNiAyLTEzIDMtMjAgMi04IDctMTUgMTUtMThsMTUtM2MxMiAwIDE0LTIgMjMtOWE0MSA0MSAwIDAgMSAxMy05IDI3IDI3IDAgMCAxIDE2IDB6bTg3IDI1N2wtMjQtNC0xMSAyMWgtMWMtNSA2LTkgNi0xMyAzcy03LTExLTktMTV2LTJsLTI1LTQ3YTMgMyAwIDAgMSAxLTRsOS01IDEwLTlhMyAzIDAgMCAxIDIgMGM3IDAgMTQtMSAxOS0zIDYtMiAxMS01IDE2LTExaDRsMSAxIDI5IDU1YzIgNSA1IDExIDIgMTYtMSAzLTQgNS05IDRoLTF6bS0xNjYtNGwtMjQgNGgtMWMtNSAxLTgtMS05LTRsLTEtOCAzLTggMjktNTYgMS0xYzEtMSAzIDAgNCAxIDQgNiAxMCA5IDE1IDExIDYgMiAxMyAzIDIwIDJsMiAxIDEwIDkgOSA1YTMgMyAwIDAgMSAxIDRsLTI1IDQ3djFhMzkgMzkgMCAwIDEtOSAxNmMtNCAzLTggMy0xMy0zaC0xbC0xMS0yMXptMTAwLTIwM2E1MSA1MSAwIDAgMC04MCA0MiA1MSA1MSAwIDEgMCAxMDIgMCA1MiA1MiAwIDAgMC03LTI2IDUxIDUxIDAgMCAwLTE1LTE2ek00NDQgMzlhMTcgMTcgMCAxIDEgMCAzNCAxNyAxNyAwIDAgMSAwLTM0em0tMTE2IDBhMTcgMTcgMCAxIDEgMCAzNCAxNyAxNyAwIDAgMSAwLTM0em01OCAwYTE3IDE3IDAgMSAxIDAgMzQgMTcgMTcgMCAwIDEgMC0zNHoiLz48L3N2Zz4=\" alt=\"License\"></a>\n</div>\n\n## ⚡️ Introduction\n\n**bit7z** is a _cross-platform_ C++ static library that allows the _compression/extraction of archive files_ through a _clean_ and _simple_ wrapper interface to the dynamic libraries from the [7-zip](https://www.7-zip.org/ \"7-zip Project Homepage\") project.<br/>\nIt supports compression and extraction to and from the filesystem or the memory, reading archives metadata, updating existing ones, creating multi-volume archives, operation progress callbacks, and many other functionalities.\n\n## 🎯 Supported Features\n\n+ **Compression** using the following archive formats: **7z**, XZ, **BZIP2**, **GZIP**, TAR, **ZIP**, and WIM.\n+ **Extraction** of many archive formats: **7z**, AR, ARJ, **BZIP2**, CAB, CHM, CPIO, CramFS, DEB, DMG, EXT, FAT, GPT, **GZIP**, HFS, HXS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, **RAR**, **RAR5**, RPM, SquashFS, TAR, UDF, UEFI, VDI, VHD, VMDK, WIM, XAR, XZ, Z, and **ZIP**.\n+ **Reading metadata** of archives and their content.\n+ **Testing** archives for errors.\n+ **Updating** existing file archives with new files.\n+ **Renaming**, **updating**, or **deleting** old items in existing file archives.\n+ **Compression and extraction _to and from_ memory** and **C++ standard streams**.\n+ Compression using **custom path aliases** for the items in the output archives.\n+ **Selective extraction** of only specified files/folders **using wildcards** and **regular expressions**.\n+ Creation of **encrypted archives** (strong AES-256 encryption &mdash; only for 7z and ZIP formats).\n+ **Archive header encryption** (only for 7z format).\n+ Possibility to choose the **compression level** (if supported by the archive format), the **compression method** ([supported methods](https://github.com/rikyoz/bit7z/wiki/Advanced-Usage#compression-methods \"Wiki page on bit7z's supported compression methods\")), the **dictionary size**, and the **word size**.\n+ **Automatic input archive format detection**.\n+ **Solid archives** (only for 7z).\n+ **Multi-volume archives**.\n+ **Operation callbacks** for obtaining real-time information about ongoing operations.\n+ **Canceling** or **pausing** the current operation.\n\n### Notes\n\nThe presence or not of some of the above features depends on the particular shared library used along with bit7z.<br/>\nFor example, 7z.dll should support all these features, 7za.dll should work only with the 7z file format, and 7zxa.dll can only extract 7z files. For more information about the 7-zip DLLs, please check this [wiki page](https://github.com/rikyoz/bit7z/wiki/7z-DLLs).\n\nIn the end, some other features (e.g., _automatic format detection_ and _selective extraction using regular expressions_) are disabled by default, and macro definitions must be used during compilation to have them available ([wiki](https://github.com/rikyoz/bit7z/wiki/Building-the-library)).\n\n## 🔥 Getting Started (Library Usage)\n\nBelow are a few examples that show how to use some of the main features of bit7z.\n\n### 📂 Extracting files from an archive\n\n```cpp\n#include <bit7z/bitfileextractor.hpp>\n\ntry { // bit7z classes can throw BitException objects\n    using namespace bit7z;\n\n    Bit7zLibrary lib{ \"7za.dll\" };\n    BitFileExtractor extractor{ lib, BitFormat::SevenZip };\n\n    // Extracting a simple archive\n    extractor.extract( \"path/to/archive.7z\", \"out/dir/\" );\n\n    // Extracting a specific file\n    extractor.extractMatching( \"path/to/archive.7z\", \"file.pdf\", \"out/dir/\" );\n\n    // Extracting the first file of an archive to a buffer\n    std::vector< byte_t > buffer;\n    extractor.extract( \"path/to/archive.7z\", buffer );\n\n    // Extracting an encrypted archive\n    extractor.setPassword( \"password\" );\n    extractor.extract( \"path/to/another/archive.7z\", \"out/dir/\" );\n} catch ( const bit7z::BitException& ex ) { /* Do something with ex.what()...*/ }\n```\n\nAlternatively, if you only need to work on a single archive:\n\n```cpp\n#include <bit7z/bitarchivereader.hpp>\n\ntry { // bit7z classes can throw BitException objects\n    using namespace bit7z;\n\n    Bit7zLibrary lib{ \"7z.dll\" };\n\n    // Opening the archive\n    BitArchiveReader reader{lib, \"path/to/archive.gz\", BitFormat::GZip };\n\n    // Testing the archive\n    reader.test();\n\n    // Extracting the archive\n    reader.extract( \"out/dir/\" );\n} catch ( const bit7z::BitException& ex ) { /* Do something with ex.what()...*/ }\n```\n\n### 💼 Compressing files into an archive\n\n```cpp\n#include <bit7z/bitfilecompressor.hpp>\n\ntry { // bit7z classes can throw BitException objects\n    using namespace bit7z;\n\n    Bit7zLibrary lib{ \"7z.dll\" };\n    BitFileCompressor compressor{ lib, BitFormat::Zip };\n\n    std::vector< std::string > files = { \"path/to/file1.jpg\", \"path/to/file2.pdf\" };\n\n    // Creating a simple zip archive\n    compressor.compress( files, \"output_archive.zip\" );\n\n    // Creating a zip archive with a custom directory structure\n    std::map< std::string, std::string > files_map = {\n        { \"path/to/file1.jpg\", \"alias/path/file1.jpg\" },\n        { \"path/to/file2.pdf\", \"alias/path/file2.pdf\" }\n    };\n    compressor.compress( files_map, \"output_archive2.zip\" );\n\n    // Compressing a directory\n    compressor.compressDirectory( \"dir/path/\", \"dir_archive.zip\" );\n\n    // Creating an encrypted zip archive of two files\n    compressor.setPassword( \"password\" );\n    compressor.compressFiles( files, \"protected_archive.zip\" );\n\n    // Updating an existing zip archive\n    compressor.setUpdateMode( UpdateMode::Append );\n    compressor.compressFiles( files, \"existing_archive.zip\" );\n\n    // Compressing a single file into a buffer\n    std::vector< bit7z::byte_t > buffer;\n    BitFileCompressor compressor2{ lib, BitFormat::BZip2 };\n    compressor2.compressFile( files[0], buffer );\n} catch ( const bit7z::BitException& ex ) { /* Do something with ex.what()...*/ }\n```\n\nAlternatively, if you only need to work on a single archive:\n\n```cpp\n#include <bit7z/bitarchivewriter.hpp>\n\ntry { // bit7z classes can throw BitException objects\n    using namespace bit7z;\n\n    Bit7zLibrary lib{ \"7z.dll\" };\n    BitArchiveWriter writer{lib, BitFormat::SevenZip };\n\n    // Adding the items to be compressed (no compression is performed here)\n    writer.addFile( \"path/to/file.txt\" );\n    writer.addDirectory( \"path/to/dir/\" );\n\n    // Compressing the added items to the output archive\n    writer.compressTo( \"output.7z\" );\n} catch ( const bit7z::BitException& ex ) { /* Do something with ex.what()...*/ }\n```\n\n### 📑 Reading archive metadata\n\n```cpp\n#include <bit7z/bitarchivereader.hpp>\n\ntry { // bit7z classes can throw BitException objects\n    using namespace bit7z;\n\n    Bit7zLibrary lib{ \"7za.dll\" };\n    BitArchiveReader arc{ lib, \"archive.7z\", BitFormat::SevenZip };\n\n    // Printing archive metadata\n    std::cout << \"Archive properties\" << std::endl;\n    std::cout << \"  Items count: \"   << arc.itemsCount() << std::endl;\n    std::cout << \"  Folders count: \" << arc.foldersCount() << std::endl;\n    std::cout << \"  Files count: \"   << arc.filesCount() << std::endl;\n    std::cout << \"  Size: \"          << arc.size() << std::endl;\n    std::cout << \"  Packed size: \"   << arc.packSize() << std::endl;\n    std::cout << std::endl;\n\n    // Printing the metadata of the archived items\n    std::cout << \"Archived items\";\n    auto arc_items = arc.items();\n    for ( auto& item : arc_items ) {\n        std::cout << std::endl;\n        std::cout << \"  Item index: \"   << item.index() << std::endl;\n        std::cout << \"    Name: \"        << item.name() << std::endl;\n        std::cout << \"    Extension: \"   << item.extension() << std::endl;\n        std::cout << \"    Path: \"        << item.path() << std::endl;\n        std::cout << \"    IsDir: \"       << item.isDir() << std::endl;\n        std::cout << \"    Size: \"        << item.size() << std::endl;\n        std::cout << \"    Packed size: \" << item.packSize() << std::endl;\n        std::cout << \"    CRC: \"         << item.crc() << std::endl;\n    }\n} catch ( const bit7z::BitException& ex ) { /* Do something with ex.what()...*/ }\n```\n\nA complete _**API reference**_ is available in the [wiki](https://github.com/rikyoz/bit7z/wiki/) section.\n\n## 🚀 Upgrading from bit7z v3 to v4\n\n<details>\n  <summary>Expand for more details!</summary>\n\nThe newest bit7z v4 introduced some significant breaking changes to the API. In particular:\n\n+ By default, the project now follows the [UTF-8 Everywhere Manifesto](http://utf8everywhere.org/):\n  + The default string type is `std::string` (instead of `std::wstring`), so users can use the library in cross-platform projects more easily (v4 introduced Linux/macOS support too).\n  + Input `std::string`s will be considered as UTF-8 encoded.\n  + You can still achieve the old behavior on Windows using the `-DBIT7Z_USE_NATIVE_STRING` CMake option.\n+ The old `BitExtractor` class is now called `BitFileExtractor`.\n  + Now `BitExtractor` is just the name of a template class for all the extraction classes.\n+ The `ProgressCallback` now must return a `bool` value indicating whether the current operation can continue (`true`) or not (`false`).\n\n</details>\n\n## 💾 Download\n\n<div align=\"center\">\n<a href=\"https://github.com/rikyoz/bit7z/releases\">\n<img alt=\"GitHub Latest Release\" src=\"https://img.shields.io/github/v/release/rikyoz/bit7z?include_prereleases&sort=semver&label=Latest%20Release&style=social\" height='36' style='border:0;height:36px;'/></a>\n&emsp;&emsp;\n<a href=\"https://github.com/rikyoz/bit7z/releases/latest\">\n<img alt=\"GitHub Stable Release\" src=\"https://img.shields.io/github/v/release/rikyoz/bit7z?label=Stable%20Release&style=social\" height='36' style='border:0;height:36px;'/></a>\n<br/>\n<a href=\"https://github.com/rikyoz/bit7z/releases\">\n<img alt=\"GitHub All Releases\" src=\"https://img.shields.io/github/downloads/rikyoz/bit7z/total.svg?style=popout&label=Total%20Downloads&logo=icloud&logoColor=white\"/></a>\n</div>\n\nEach released package contains:\n\n+ A _pre-compiled version_ of bit7z (both in _debug_ and _release_ mode);\n+ The _public API headers_ needed to use the library in your program;\n\nPackages are available for both _x86_ and _x64_ architectures.\n\nYou can also clone/download this repository and build the library by yourself (please, read the [wiki](https://github.com/rikyoz/bit7z/wiki/Building-the-library)).\n\n## 🧰 Requirements\n\n+ **Operating System:** Windows, Linux, macOS[^1].\n+ **Architecture:** x86, x86_64.\n+ **Compiler:** MSVC 2015 or later[^2], MinGW v6.4 or later, GCC v4.9 or later, Clang 3.5 or later.\n+ **Shared Library:** a 7-zip `.dll` library on Windows, a 7-zip/p7zip `.so` library on Unix[^3].\n\n[^1]: On Windows, you should link your program _also_ with _oleaut32_ (e.g., `-lbit7z -loleaut32`).<br/> On Linux and macOS, you should link your program _also_ with _dl_ (e.g., `-lbit7z -ldl`).<br/> If you are using the library via CMake, these dependencies will be linked automatically to your project.\n\n[^2]: MSVC 2010 was supported until v2.x, MSVC 2012/2013 until v3.x.\n\n[^3]: bit7z doesn't ship with the 7-zip shared libraries. You can build them from the source code available at [7-zip.org](http://www.7-zip.org/).\n\n## ⚙️ Building and using bit7z\n\nFor building the library:\n\n```bash\ncd <bit7z folder>\nmkdir build && cd build\ncmake ../ -DCMAKE_BUILD_TYPE=Release\ncmake --build . -j --config Release\n```\n\nA more detailed guide on how to build this library is available [here](https://github.com/rikyoz/bit7z/wiki/Building-the-library).\n\nYou can directly integrate the library into your project via CMake:\n\n+ Download bit7z and copy it into a sub-directory of your project (e.g., `third_party`), or add it as a git submodule of your repository.\n+ You can then use the command `add_subdirectory()` in your `CMakeLists.txt` to include bit7z.\n+ Finally, add the `bit7z64` target (just `bit7z` on x86) using the `target_link_libraries()` command.\n\nFor example:\n\n```cmake\nadd_subdirectory( ${CMAKE_SOURCE_DIR}/third_party/bit7z )\ntarget_link_libraries( ${TARGET_NAME} PRIVATE bit7z64 ) # or bit7z on x86\n```\n\n## ☕️ Donate\n\nIf you have found this project helpful, please consider supporting me with a small donation so that I can keep improving it!\nThank you! :pray:\n\n<div align=\"center\">\n<a href='https://github.com/sponsors/rikyoz' target='_blank' title=\"Support the Maintainer via GitHub Sponsors\"><img height='24' style='border:0px;height:24px;' src='https://img.shields.io/badge/-Sponsor%20the%20Maintainer-fafbfc?logo=GitHub%20Sponsors' border='0' alt='Sponsor me on GitHub' /></a>&thinsp;<a href='https://ko-fi.com/G2G1LS1P' target='_blank' title=\"Support this project via Ko-Fi\"><img height='24' style='border:0px;height:24px;' src='https://img.shields.io/badge/-Buy%20Me%20a%20Coffee-red?logo=ko-fi&logoColor=white' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>&thinsp;<a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NTZF5G7LRXDRC\" title=\"Support this project via PayPal\"><img src=\"https://img.shields.io/badge/-Donate%20on%20PayPal-yellow.svg?logo=paypal&logoColor=white\" alt=\"Donations\" height='24' style='border:0px;height:24px;'></a>\n</div>\n\n## 📜 License\n\nThis project is licensed under the terms of the [Mozilla Public License v2.0](https://www.mozilla.org/en-US/MPL/2.0/).<br/>\nFor more details, please check:\n\n+ The [LICENSE](LICENSE) file.\n+ [Mozilla's MPL-2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/)\n\nOlder versions (v3.x and earlier) of bit7z were released under the [GNU General Public License v2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).\n\n<br/>\n<div align=\"center\">\nCopyright &copy; 2014 - 2023 Riccardo Ostani (<a href=\"https://github.com/rikyoz\">@rikyoz</a>)\n</div>\n<br/>\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bit7z.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BIT7Z_HPP\n#define BIT7Z_HPP\n\n#include \"bitarchivereader.hpp\"\n#include \"bitexception.hpp\"\n#include \"bitfilecompressor.hpp\"\n#include \"bitfileextractor.hpp\"\n#include \"bitmemcompressor.hpp\"\n#include \"bitmemextractor.hpp\"\n#include \"bitstreamcompressor.hpp\"\n#include \"bitstreamextractor.hpp\"\n\n#endif // BIT7Z_HPP\n\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bit7zlibrary.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BIT7ZLIBRARY_HPP\n#define BIT7ZLIBRARY_HPP\n\n#include <string>\n\n#include \"bittypes.hpp\"\n#include \"bitwindows.hpp\"\n\nstruct IInArchive;\nstruct IOutArchive;\n\n#ifndef _WIN32\nstruct GUID;\n#endif\n\n//! @cond IGNORE_BLOCK_IN_DOXYGEN\ntemplate< typename T >\nclass CMyComPtr;\n//! @endcond\n\n/**\n * @brief The main namespace of the bit7z library.\n */\nnamespace bit7z {\n\n/**\n * @brief The default file path for the 7-zip shared library to be used by bit7z\n * in case the user doesn't pass a path to the constructor of the Bit7zLibrary class.\n *\n * @note On Windows, the default library is \"7z.dll\", and it is searched following the Win32 API rules\n * (https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order).\n *\n * @note On Linux, the default library is the absolute path to the \"7z.so\" installed by p7zip.\n *\n * @note In all other cases, the value will be the relative path to a \"7z.so\" in the working directory of the program.\n */\n#ifdef __DOXYGEN__\nconstexpr auto default_library = \"<platform-dependent value>\";\n#elif defined( _WIN32 )\nconstexpr auto default_library = BIT7Z_STRING( \"7z.dll\" );\n#elif defined( __linux__ )\nconstexpr auto default_library = \"/usr/lib/p7zip/7z.so\"; // Default installation path of the p7zip shared library.\n#else\nconstexpr auto default_library = \"./7z.so\";\n#endif\n\n/**\n * @brief The Bit7zLibrary class allows accessing the basic functionalities provided by the 7z DLLs.\n */\nclass Bit7zLibrary final {\n    public:\n        Bit7zLibrary( const Bit7zLibrary& ) = delete;\n\n        Bit7zLibrary( Bit7zLibrary&& ) = delete;\n\n        Bit7zLibrary& operator=( const Bit7zLibrary& ) = delete;\n\n        Bit7zLibrary& operator=( Bit7zLibrary&& ) = delete;\n\n        /**\n         * @brief Constructs a Bit7zLibrary object by loading the specified 7zip shared library.\n         *\n         * By default, it searches a 7z.dll in the same path of the application.\n         *\n         * @param library_path  the path to the shared library file to be loaded.\n         */\n        explicit Bit7zLibrary( const tstring& library_path = default_library );\n\n        /**\n         * @brief Destructs the Bit7zLibrary object, freeing the loaded shared library.\n         */\n        ~Bit7zLibrary();\n\n        /**\n         * @brief Initiates the 7-zip object needed to create a new archive or use an old one.\n         *\n         * @note Usually, this method should not be called directly by users of the bit7z library.\n         *\n         * @param format_ID     Pointer to the GUID of the archive format (see BitInFormat's guid() method).\n         * @param interface_ID  Pointer to the GUID of the archive interface to be requested\n         *                      (IID_IInArchive or IID_IOutArchive).\n         * @param out_object    Pointer to a CMyComPtr of an object implementing the requested interface.\n         */\n        void createArchiveObject( const GUID* format_ID, const GUID* interface_ID, void** out_object ) const;\n\n        /**\n         * @brief Set the 7-zip shared library to use large memory pages.\n         */\n        void setLargePageMode();\n\n    private:\n        using CreateObjectFunc = HRESULT ( WINAPI* )( const GUID* clsID, const GUID* interfaceID, void** out );\n\n        HMODULE mLibrary;\n        CreateObjectFunc mCreateObjectFunc;\n};\n\n}  // namespace bit7z\n\n#endif // BIT7ZLIBRARY_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitabstractarchivecreator.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITABSTRACTARCHIVECREATOR_HPP\n#define BITABSTRACTARCHIVECREATOR_HPP\n\n#include <map>\n#include <memory>\n\n#include \"bitabstractarchivehandler.hpp\"\n#include \"bitcompressionlevel.hpp\"\n#include \"bitcompressionmethod.hpp\"\n#include \"bitformat.hpp\"\n#include \"bitinputarchive.hpp\"\n\nstruct IOutStream;\nstruct ISequentialOutStream;\n\nnamespace bit7z {\n\nusing std::ostream;\n\nclass ArchiveProperties;\n\n/**\n * @brief Enumeration representing how an archive creator should deal when the output archive already exists.\n */\nenum struct UpdateMode {\n    None, ///< The creator will throw an exception (unless the OverwriteMode is not None).\n    Append, ///< The creator will append the new items to the existing archive.\n    Update, ///< New items whose path already exists in the archive will overwrite the old ones, other will be appended.\n    BIT7Z_DEPRECATED_ENUMERATOR( Overwrite, Update, \"Since v4.0; please use the UpdateMode::Update enumerator.\" ) ///< @deprecated since v4.0; please use the UpdateMode::Update enumerator.\n};\n\n/**\n * @brief Abstract class representing a generic archive creator.\n */\nclass BitAbstractArchiveCreator : public BitAbstractArchiveHandler {\n    public:\n        BitAbstractArchiveCreator( const BitAbstractArchiveCreator& ) = delete;\n\n        BitAbstractArchiveCreator( BitAbstractArchiveCreator&& ) = delete;\n\n        BitAbstractArchiveCreator& operator=( const BitAbstractArchiveCreator& ) = delete;\n\n        BitAbstractArchiveCreator& operator=( BitAbstractArchiveCreator&& ) = delete;\n\n        ~BitAbstractArchiveCreator() override = default;\n\n        /**\n         * @return the format used for creating/updating an archive.\n         */\n        BIT7Z_NODISCARD const BitInFormat& format() const noexcept override;\n\n        /**\n         * @return the format used for creating/updating an archive.\n         */\n        BIT7Z_NODISCARD const BitInOutFormat& compressionFormat() const noexcept;\n\n        /**\n         * @return whether the creator crypts also the headers of archives or not.\n         */\n        BIT7Z_NODISCARD bool cryptHeaders() const noexcept;\n\n        /**\n         * @return the compression level used for creating/updating an archive.\n         */\n        BIT7Z_NODISCARD BitCompressionLevel compressionLevel() const noexcept;\n\n        /**\n         * @return the compression method used for creating/updating an archive.\n         */\n        BIT7Z_NODISCARD BitCompressionMethod compressionMethod() const noexcept;\n\n        /**\n         * @return the dictionary size used for creating/updating an archive.\n         */\n        BIT7Z_NODISCARD uint32_t dictionarySize() const noexcept;\n\n        /**\n         * @return the word size used for creating/updating an archive.\n         */\n        BIT7Z_NODISCARD uint32_t wordSize() const noexcept;\n\n        /**\n         * @return whether the archive creator uses solid compression or not.\n         */\n        BIT7Z_NODISCARD bool solidMode() const noexcept;\n\n        /**\n         * @return the update mode used when updating existing archives.\n         */\n        BIT7Z_NODISCARD UpdateMode updateMode() const noexcept;\n\n        /**\n         * @return the volume size (in bytes) used when creating multi-volume archives\n         *         (a 0 value means that all files are going in a single archive).\n         */\n        BIT7Z_NODISCARD uint64_t volumeSize() const noexcept;\n\n        /**\n         * @return the number of threads used when creating/updating an archive\n         *         (a 0 value means that it will use the 7-zip default value).\n         */\n        BIT7Z_NODISCARD uint32_t threadsCount() const noexcept;\n\n        /**\n         * @brief Sets up a password for the output archives.\n         *\n         * When setting a password, the produced archives will be encrypted using the default\n         * cryptographic method of the output format. The option \"crypt headers\" remains unchanged,\n         * in contrast with what happens when calling the setPassword(tstring, bool) method.\n         *\n         * @note Calling setPassword when the output format doesn't support archive encryption\n         * (e.g., GZip, BZip2, etc...) does not have any effects (in other words, it doesn't\n         * throw exceptions, and it has no effects on compression operations).\n         *\n         * @note After a password has been set, it will be used for every subsequent operation.\n         * To disable the use of the password, you need to call the clearPassword method\n         * (inherited from BitAbstractArchiveHandler), which is equivalent to setPassword(L\"\").\n         *\n         * @param password the password to be used when creating/updating archives.\n         */\n        void setPassword( const tstring& password ) override;\n\n        /**\n         * @brief Sets up a password for the output archive.\n         *\n         * When setting a password, the produced archive will be encrypted using the default\n         * cryptographic method of the output format. If the format is 7z, and the option\n         * \"crypt_headers\" is set to true, the headers of the archive will be encrypted,\n         * resulting in a password request every time the output file will be opened.\n         *\n         * @note Calling setPassword when the output format doesn't support archive encryption\n         * (e.g., GZip, BZip2, etc...) does not have any effects (in other words, it doesn't\n         * throw exceptions, and it has no effects on compression operations).\n         *\n         * @note Calling setPassword with \"crypt_headers\" set to true does not have effects on\n         * formats different from 7z.\n         *\n         * @note After a password has been set, it will be used for every subsequent operation.\n         * To disable the use of the password, you need to call the clearPassword method\n         * (inherited from BitAbstractArchiveHandler), which is equivalent to setPassword(L\"\").\n         *\n         * @param password          the password to be used when creating/updating archives.\n         * @param crypt_headers     if true, the headers of the output archives will be encrypted\n         *                          (valid only when using the 7z format).\n         */\n        void setPassword( const tstring& password, bool crypt_headers );\n\n        /**\n         * @brief Sets the compression level to be used when creating/updating an archive.\n         *\n         * @param level the compression level desired.\n         */\n        void setCompressionLevel( BitCompressionLevel level ) noexcept;\n\n        /**\n         * @brief Sets the compression method to be used when creating/updating an archive.\n         *\n         * @param method the compression method desired.\n         */\n        void setCompressionMethod( BitCompressionMethod method );\n\n        /**\n         * @brief Sets the dictionary size to be used when creating/updating an archive.\n         *\n         * @param dictionary_size the dictionary size desired.\n         */\n        void setDictionarySize( uint32_t dictionary_size );\n\n        /**\n         * @brief Sets the word size to be used when creating/updating an archive.\n         *\n         * @param word_size the word size desired.\n         */\n        void setWordSize( uint32_t word_size );\n\n        /**\n         * @brief Sets whether to use solid compression or not.\n         *\n         * @note Setting the solid compression mode to true has effect only when using the 7z format with multiple\n         * input files.\n         *\n         * @param solid_mode    if true, it will be used the \"solid compression\" method.\n         */\n        void setSolidMode( bool solid_mode ) noexcept;\n\n        /**\n         * @brief Sets whether and how the creator can update existing archives or not.\n         *\n         * @note If set to UpdateMode::None, a subsequent compression operation may throw an exception\n         *       if it targets an existing archive.\n         *\n         * @param mode the desired update mode.\n         */\n        virtual void setUpdateMode( UpdateMode mode );\n\n        /**\n         * @brief Sets whether the creator can update existing archives or not.\n         *\n         * @deprecated since v4.0; it is provided just for an easier transition from the old v3 API.\n         *\n         * @note If set to false, a subsequent compression operation may throw an exception\n         *       if it targets an existing archive.\n         *\n         * @param can_update if true, compressing operations will update existing archives.\n         */\n        BIT7Z_DEPRECATED_MSG( \"Since v4.0; please use the overloaded function that takes an UpdateMode enumerator.\" )\n        void setUpdateMode( bool can_update );\n\n        /**\n         * @brief Sets the volume_size (in bytes) of the output archive volumes.\n         *\n         * @note This setting has effects only when the destination archive is on the filesystem.\n         *\n         * @param volume_size    The dimension of a volume.\n         */\n        void setVolumeSize( uint64_t volume_size ) noexcept;\n\n        /**\n         * @brief Sets the number of threads to be used when creating/updating an archive.\n         *\n         * @param threads_count the number of threads desired.\n         */\n        void setThreadsCount( uint32_t threads_count ) noexcept;\n\n        /**\n         * @brief Sets a property for the output archive format as described by the 7-zip documentation\n         * (e.g. https://sevenzip.osdn.jp/chm/cmdline/switches/method.htm).\n         *\n         * @tparam T    An integral type (i.e., a bool or an integer type).\n         *\n         * @param name  The string name of the property to be set.\n         * @param value The value to be used for the property.\n         */\n        template< std::size_t N, typename T, typename = typename std::enable_if< std::is_integral< T >::value >::type >\n        void setFormatProperty( const wchar_t (&name)[N], T value ) noexcept { // NOLINT(*-avoid-c-arrays)\n            mExtraProperties[ name ] = value;\n        }\n\n        /**\n         * @brief Sets a property for the output archive format as described by the 7-zip documentation\n         * (e.g. https://sevenzip.osdn.jp/chm/cmdline/switches/method.htm).\n         *\n         * For example, passing the string L\"tm\" with a false value while creating a .7z archive\n         * will disable storing the last modified timestamps of the compressed files.\n         *\n         * @tparam T    A non-integral type (i.e., a string).\n         *\n         * @param name  The string name of the property to be set.\n         * @param value The value to be used for the property.\n         */\n        template< std::size_t N, typename T, typename = typename std::enable_if< !std::is_integral< T >::value >::type >\n        void setFormatProperty( const wchar_t (&name)[N], const T& value ) noexcept { // NOLINT(*-avoid-c-arrays)\n            mExtraProperties[ name ] = value;\n        }\n\n    protected:\n        BitAbstractArchiveCreator( const Bit7zLibrary& lib,\n                                   const BitInOutFormat& format,\n                                   tstring password = {},\n                                   UpdateMode update_mode = UpdateMode::None );\n\n        BIT7Z_NODISCARD ArchiveProperties archiveProperties() const;\n\n        friend class BitOutputArchive;\n\n    private:\n        const BitInOutFormat& mFormat;\n\n        UpdateMode mUpdateMode;\n        BitCompressionLevel mCompressionLevel;\n        BitCompressionMethod mCompressionMethod;\n        uint32_t mDictionarySize;\n        uint32_t mWordSize;\n        bool mCryptHeaders;\n        bool mSolidMode;\n        uint64_t mVolumeSize;\n        uint32_t mThreadsCount;\n        std::map< std::wstring, BitPropVariant > mExtraProperties;\n};\n\n}  // namespace bit7z\n\n#endif // BITABSTRACTARCHIVECREATOR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitabstractarchivehandler.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITABSTRACTARCHIVEHANDLER_HPP\n#define BITABSTRACTARCHIVEHANDLER_HPP\n\n#include <cstdint>\n#include <functional>\n\n#include \"bit7zlibrary.hpp\"\n#include \"bitdefines.hpp\"\n\nnamespace bit7z {\n\nusing std::function;\n\nclass BitInFormat;\n\n/**\n * @brief A std::function whose argument is the total size of the ongoing operation.\n */\nusing TotalCallback = function< void( uint64_t ) >;\n\n/**\n * @brief A std::function whose argument is the currently processed size of the ongoing operation and returns\n *        true or false whether the operation must continue or not.\n */\nusing ProgressCallback = function< bool( uint64_t ) >;\n\n/**\n * @brief A std::function whose arguments are the current processed input size, and the current output size of the\n *        ongoing operation.\n */\nusing RatioCallback = function< void( uint64_t, uint64_t ) >;\n\n/**\n * @brief A std::function whose argument is the path, in the archive, of the file currently being processed\n *        by the ongoing operation.\n */\nusing FileCallback = function< void( tstring ) >;\n\n/**\n * @brief A std::function returning the password to be used to handle an archive.\n */\nusing PasswordCallback = function< tstring() >;\n\n/**\n * @brief Enumeration representing how a handler should deal when an output file already exists.\n */\nenum struct OverwriteMode {\n    None = 0, ///< The handler will throw an exception if the output file or buffer already exists.\n    Overwrite, ///< The handler will overwrite the old file or buffer with the new one.\n    Skip, ///< The handler will skip writing to the output file or buffer.\n//TODO:    RenameOutput,\n//TODO:    RenameExisting\n};\n\n/**\n * @brief Abstract class representing a generic archive handler.\n */\nclass BitAbstractArchiveHandler {\n    public:\n        BitAbstractArchiveHandler( const BitAbstractArchiveHandler& ) = delete;\n\n        BitAbstractArchiveHandler( BitAbstractArchiveHandler&& ) = delete;\n\n        BitAbstractArchiveHandler& operator=( const BitAbstractArchiveHandler& ) = delete;\n\n        BitAbstractArchiveHandler& operator=( BitAbstractArchiveHandler&& ) = delete;\n\n        virtual ~BitAbstractArchiveHandler() = default;\n\n        /**\n         * @return the Bit7zLibrary object used by the handler.\n         */\n        BIT7Z_NODISCARD const Bit7zLibrary& library() const noexcept;\n\n        /**\n         * @return the format used by the handler for extracting or compressing.\n         */\n        BIT7Z_NODISCARD virtual const BitInFormat& format() const = 0;\n\n        /**\n         * @return the password used to open, extract, or encrypt the archive.\n         */\n        BIT7Z_NODISCARD tstring password() const;\n\n        /**\n         * @return a boolean value indicating whether the directory structure must be preserved while extracting\n         * or compressing the archive.\n         */\n        BIT7Z_NODISCARD bool retainDirectories() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether a password is defined or not.\n         */\n        BIT7Z_NODISCARD bool isPasswordDefined() const noexcept;\n\n        /**\n         * @return the current total callback.\n         */\n        BIT7Z_NODISCARD TotalCallback totalCallback() const;\n\n        /**\n         * @return the current progress callback.\n         */\n        BIT7Z_NODISCARD ProgressCallback progressCallback() const;\n\n        /**\n         * @return the current ratio callback.\n         */\n        BIT7Z_NODISCARD RatioCallback ratioCallback() const;\n\n        /**\n         * @return the current file callback.\n         */\n        BIT7Z_NODISCARD FileCallback fileCallback() const;\n\n        /**\n         * @return the current password callback.\n         */\n        BIT7Z_NODISCARD PasswordCallback passwordCallback() const;\n\n        /**\n         * @return the current OverwriteMode.\n         */\n        BIT7Z_NODISCARD OverwriteMode overwriteMode() const;\n\n        /**\n         * @brief Sets up a password to be used by the archive handler.\n         *\n         * The password will be used to encrypt/decrypt archives by using the default\n         * cryptographic method of the archive format.\n         *\n         * @note Calling setPassword when the input archive is not encrypted does not have any effect on\n         * the extraction process.\n         *\n         * @note Calling setPassword when the output format doesn't support archive encryption\n         * (e.g., GZip, BZip2, etc...) does not have any effects (in other words, it doesn't\n         * throw exceptions, and it has no effects on compression operations).\n         *\n         * @note After a password has been set, it will be used for every subsequent operation.\n         * To disable the use of the password, you need to call the clearPassword method, which is equivalent\n         * to calling setPassword(L\"\").\n         *\n         * @param password  the password to be used.\n         */\n        virtual void setPassword( const tstring& password );\n\n        /**\n         * @brief Clear the current password used by the handler.\n         *\n         * Calling clearPassword() will disable the encryption/decryption of archives.\n         *\n         * @note This is equivalent to calling setPassword(L\"\").\n         */\n        void clearPassword() noexcept;\n\n        /**\n         * @brief Sets whether the operations' output will preserve the input's directory structure or not.\n         *\n         * @param retain  the setting for preserving or not the input directory structure\n         */\n        void setRetainDirectories( bool retain ) noexcept;\n\n        /**\n         * @brief Sets the function to be called when the total size of an operation is available.\n         *\n         * @param callback  the total callback to be used.\n         */\n        void setTotalCallback( const TotalCallback& callback );\n\n        /**\n         * @brief Sets the function to be called when the processed size of the ongoing operation is updated.\n         *\n         * @note The completion percentage of the current operation can be obtained by calculating\n         * `static_cast<int>( ( 100.0 * processed_size ) / total_size )`.\n         *\n         * @param callback  the progress callback to be used.\n         */\n        void setProgressCallback( const ProgressCallback& callback );\n\n        /**\n         * @brief Sets the function to be called when the input processed size and current output size of the\n         * ongoing operation are known.\n         *\n         * @note The ratio percentage of a compression operation can be obtained by calculating\n         * `static_cast<int>( ( 100.0 * output_size ) / input_size )`.\n         *\n         * @param callback  the ratio callback to be used.\n         */\n        void setRatioCallback( const RatioCallback& callback );\n\n        /**\n         * @brief Sets the function to be called when the current file being processed changes.\n         *\n         * @param callback  the file callback to be used.\n         */\n        void setFileCallback( const FileCallback& callback );\n\n        /**\n         * @brief Sets the function to be called when a password is needed to complete the ongoing operation.\n         *\n         * @param callback  the password callback to be used.\n         */\n        void setPasswordCallback( const PasswordCallback& callback );\n\n        /**\n         * @brief Sets how the handler should behave when it tries to output to an existing file or buffer.\n         *\n         * @param mode  the OverwriteMode to be used by the handler.\n         */\n        void setOverwriteMode( OverwriteMode mode );\n\n    protected:\n        explicit BitAbstractArchiveHandler( const Bit7zLibrary& lib,\n                                            tstring password = {},\n                                            OverwriteMode overwrite_mode = OverwriteMode::None );\n\n    private:\n        const Bit7zLibrary& mLibrary;\n        tstring mPassword;\n        bool mRetainDirectories;\n        OverwriteMode mOverwriteMode;\n\n        //CALLBACKS\n        TotalCallback mTotalCallback;\n        ProgressCallback mProgressCallback;\n        RatioCallback mRatioCallback;\n        FileCallback mFileCallback;\n        PasswordCallback mPasswordCallback;\n};\n\n}  // namespace bit7z\n\n#endif // BITABSTRACTARCHIVEHANDLER_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitabstractarchiveopener.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITABSTRACTARCHIVEOPENER_HPP\n#define BITABSTRACTARCHIVEOPENER_HPP\n\n#include <vector>\n#include <map>\n\n#include \"bitabstractarchivehandler.hpp\"\n#include \"bitformat.hpp\"\n\nnamespace bit7z {\n\nusing std::ostream;\n\n/**\n * @brief The BitAbstractArchiveOpener abstract class represents a generic archive opener.\n */\nclass BitAbstractArchiveOpener : public BitAbstractArchiveHandler {\n    public:\n        BitAbstractArchiveOpener( const BitAbstractArchiveOpener& ) = delete;\n\n        BitAbstractArchiveOpener( BitAbstractArchiveOpener&& ) = delete;\n\n        BitAbstractArchiveOpener& operator=( const BitAbstractArchiveOpener& ) = delete;\n\n        BitAbstractArchiveOpener& operator=( BitAbstractArchiveOpener&& ) = delete;\n\n        ~BitAbstractArchiveOpener() override = default;\n\n        /**\n         * @return the archive format used by the archive opener.\n         */\n        BIT7Z_NODISCARD const BitInFormat& format() const noexcept override;\n\n        /**\n         * @return the archive format used by the archive opener.\n         */\n        BIT7Z_NODISCARD const BitInFormat& extractionFormat() const noexcept;\n\n    protected:\n        BitAbstractArchiveOpener( const Bit7zLibrary& lib,\n                                  const BitInFormat& format,\n                                  const tstring& password = {} );\n\n    private:\n        const BitInFormat& mFormat;\n};\n\n}  // namespace bit7z\n\n#endif // BITABSTRACTARCHIVEOPENER_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitarchiveeditor.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITARCHIVEEDITOR_HPP\n#define BITARCHIVEEDITOR_HPP\n\n#include <unordered_map>\n\n#include \"bitarchivewriter.hpp\"\n\nnamespace bit7z {\n\nusing std::vector;\n\nusing EditedItems = std::unordered_map< uint32_t, BitItemsVector::value_type >;\n\n/**\n * @brief The BitArchiveEditor class allows creating new file archives or updating old ones.\n *        Update operations supported are the addition of new items,\n *        as well as renaming/updating/deleting old items;\n *\n * @note  Changes are applied to the archive only after calling the applyChanges() method.\n */\nclass BIT7Z_MAYBE_UNUSED BitArchiveEditor final : public BitArchiveWriter {\n    public:\n        /**\n         * @brief Constructs a BitArchiveEditor object, reading the given archive file path.\n         *\n         * @param lib      the 7z library to use.\n         * @param in_file  the path to an input archive file.\n         * @param format   the input/output archive format.\n         * @param password (optional) the password needed to read the input archive.\n         */\n        BitArchiveEditor( const Bit7zLibrary& lib,\n                          const tstring& in_file,\n                          const BitInOutFormat& format,\n                          const tstring& password = {} );\n\n        BitArchiveEditor( const BitArchiveEditor& ) = delete;\n\n        BitArchiveEditor( BitArchiveEditor&& ) = delete;\n\n        BitArchiveEditor& operator=( const BitArchiveEditor& ) = delete;\n\n        BitArchiveEditor& operator=( BitArchiveEditor&& ) = delete;\n\n        ~BitArchiveEditor() override;\n\n        /**\n         * @brief Sets how the editor performs the update of the items in the archive.\n         *\n         * @note BitArchiveEditor doesn't support UpdateMode::None.\n         *\n         * @param mode the desired update mode (either UpdateMode::Append or UpdateMode::Overwrite).\n         */\n        void setUpdateMode( UpdateMode mode ) override;\n\n        /**\n         * @brief Requests to change the path of the item at the specified index with the given one.\n         *\n         * @param index    the index of the item to be renamed.\n         * @param new_path the new path (in the archive) desired for the item.\n         */\n        void renameItem( uint32_t index, const tstring& new_path );\n\n        /**\n         * @brief Requests to change the path of the item from old_path to the new_path.\n         *\n         * @param old_path the old path (in the archive) of the item to be renamed.\n         * @param new_path the new path (in the archive) desired for the item.\n         */\n        void renameItem( const tstring& old_path, const tstring& new_path );\n\n        /**\n         * @brief Requests to update the content of the item at the specified index\n         *        with the data from the given file.\n         *\n         * @param index    the index of the item to be updated.\n         * @param in_file  the path to the file containing the new data for the item.\n         */\n        void updateItem( uint32_t index, const tstring& in_file );\n\n        /**\n         * @brief Requests to update the content of the item at the specified index\n         *        with the data from the given buffer.\n         *\n         * @param index     the index of the item to be updated.\n         * @param in_buffer the buffer containing the new data for the item.\n         */\n        void updateItem( uint32_t index, const std::vector< byte_t >& in_buffer );\n\n        /**\n         * @brief Requests to update the content of the item at the specified index\n         *        with the data from the given stream.\n         *\n         * @param index     the index of the item to be updated.\n         * @param in_stream the stream of new data for the item.\n         */\n        void updateItem( uint32_t index, std::istream& in_stream );\n\n        /**\n         * @brief Requests to update the content of the item at the specified path\n         *        with the data from the given file.\n         *\n         * @param item_path the path (in the archive) of the item to be updated.\n         * @param in_file   the path to the file containing the new data for the item.\n         */\n        void updateItem( const tstring& item_path, const tstring& in_file );\n\n        /**\n         * @brief Requests to update the content of the item at the specified path\n         *        with the data from the given buffer.\n         *\n         * @param item_path the path (in the archive) of the item to be updated.\n         * @param in_buffer the buffer containing the new data for the item.\n         */\n        void updateItem( const tstring& item_path, const std::vector< byte_t >& in_buffer );\n\n        /**\n         * @brief Requests to update the content of the item at the specified path\n         *        with the data from the given stream.\n         *\n         * @param item_path the path (in the archive) of the item to be updated.\n         * @param in_stream the stream of new data for the item.\n         */\n        void updateItem( const tstring& item_path, istream& in_stream );\n\n        /**\n         * @brief Marks the item at the given index as deleted.\n         *\n         * @param index the index of the item to be deleted.\n         */\n        void deleteItem( uint32_t index );\n\n        /**\n         * @brief Marks the item at the given path (in the archive) as deleted.\n         *\n         * @param item_path the path (in the archive) of the item to be deleted.\n         */\n        void deleteItem( const tstring& item_path );\n\n        /**\n         * @brief Applies the requested changes (i.e., rename/update/delete operations) to the input archive.\n         */\n        void applyChanges();\n\n    private:\n        EditedItems mEditedItems;\n\n        uint32_t findItem( const tstring& item_path );\n\n        void checkIndex( uint32_t index );\n\n        BitPropVariant itemProperty( input_index index, BitProperty propID ) const override;\n\n        HRESULT itemStream( input_index index, ISequentialInStream** inStream ) const override;\n\n        bool hasNewData( uint32_t index ) const noexcept override;\n\n        bool hasNewProperties( uint32_t index ) const noexcept override;\n};\n\n}  // namespace bit7z\n\n#endif //BITARCHIVEEDITOR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitarchiveitem.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITARCHIVEITEM_HPP\n#define BITARCHIVEITEM_HPP\n\n#include \"bitgenericitem.hpp\"\n\nnamespace bit7z {\n\n/**\n * The BitArchiveItem class represents a generic item inside an archive.\n */\nclass BitArchiveItem : public BitGenericItem {\n    public:\n        /**\n         * @brief BitArchiveItem destructor.\n         */\n        ~BitArchiveItem() override = default;\n\n        /**\n         * @return the index of the item in the archive.\n         */\n        BIT7Z_NODISCARD uint32_t index() const noexcept;\n\n        /**\n         * @return true if and only if the item is a directory (i.e., it has the property BitProperty::IsDir).\n         */\n        BIT7Z_NODISCARD bool isDir() const override;\n\n        /**\n         * @return the item's name; if not available, it tries to get it from the element's path or,\n         *         if not possible, it returns an empty string.\n         */\n        BIT7Z_NODISCARD tstring name() const override;\n\n        /**\n         * @return the extension of the item, if available or if it can be inferred from the name;\n         *         otherwise it returns an empty string (e.g., when the item is a folder).\n         */\n        BIT7Z_NODISCARD tstring extension() const;\n\n        /**\n         * @return the path of the item in the archive, if available or inferable from the name, or an empty string\n         *         otherwise.\n         */\n        BIT7Z_NODISCARD tstring path() const override;\n\n        /**\n         * @return the uncompressed size of the item.\n         */\n        BIT7Z_NODISCARD uint64_t size() const override;\n\n        /**\n         * @return the item creation time.\n         */\n        BIT7Z_NODISCARD time_type creationTime() const;\n\n        /**\n         * @return the item last access time.\n         */\n        BIT7Z_NODISCARD time_type lastAccessTime() const;\n\n        /**\n         * @return the item last write time.\n         */\n        BIT7Z_NODISCARD time_type lastWriteTime() const;\n\n        /**\n         * @return the item attributes.\n         */\n        BIT7Z_NODISCARD uint32_t attributes() const override;\n\n        /**\n         * @return the compressed size of the item.\n         */\n        BIT7Z_NODISCARD uint64_t packSize() const;\n\n        /**\n         * @return the CRC value of the item.\n         */\n        BIT7Z_NODISCARD uint32_t crc() const;\n\n        /**\n         * @return true if and only if the item is encrypted.\n         */\n        BIT7Z_NODISCARD bool isEncrypted() const;\n\n    protected:\n        uint32_t mItemIndex; //Note: it is not const since the subclass BitArchiveItemOffset can increment it!\n\n        explicit BitArchiveItem( uint32_t item_index ) noexcept;\n};\n\n}  // namespace bit7z\n\n#endif // BITARCHIVEITEM_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitarchiveiteminfo.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITARCHIVEITEMINFO_HPP\n#define BITARCHIVEITEMINFO_HPP\n\n#include <map>\n\n#include \"bitarchiveitem.hpp\"\n\nnamespace bit7z {\n\nusing std::wstring;\nusing std::map;\n\n/**\n * @brief The BitArchiveItemInfo class represents an archived item and that stores all its properties for later use.\n */\nclass BitArchiveItemInfo final : public BitArchiveItem {\n    public:\n        /**\n         * @brief Gets the specified item property.\n         *\n         * @param property  the property to be retrieved.\n         *\n         * @return the value of the item property, if available, or an empty BitPropVariant.\n         */\n        BIT7Z_NODISCARD BitPropVariant itemProperty( BitProperty property ) const override;\n\n        /**\n         * @return a map of all the available (i.e., non-empty) item properties and their respective values.\n         */\n        BIT7Z_NODISCARD map< BitProperty, BitPropVariant > itemProperties() const;\n\n    private:\n        map< BitProperty, BitPropVariant > mItemProperties;\n\n        /* BitArchiveItem objects can be created and updated only by BitArchiveReader */\n        explicit BitArchiveItemInfo( uint32_t item_index );\n\n        void setProperty( BitProperty property, const BitPropVariant& value );\n\n        friend class BitArchiveReader;\n};\n\n}  // namespace bit7z\n\n#endif // BITARCHIVEITEMINFO_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitarchiveitemoffset.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITARCHIVEITEMOFFSET_HPP\n#define BITARCHIVEITEMOFFSET_HPP\n\n#include \"bitarchiveitem.hpp\"\n\nnamespace bit7z {\n\nclass BitInputArchive;\n\n/**\n * @brief The BitArchiveItemOffset class represents an archived item but doesn't store its properties.\n */\nclass BitArchiveItemOffset final : public BitArchiveItem {\n    public:\n        BitArchiveItemOffset& operator++() noexcept;\n\n        BitArchiveItemOffset operator++( int ) noexcept; // NOLINT(cert-dcl21-cpp)\n\n        bool operator==( const BitArchiveItemOffset& other ) const noexcept;\n\n        bool operator!=( const BitArchiveItemOffset& other ) const noexcept;\n\n        /**\n         * @brief Gets the specified item property.\n         *\n         * @param property  the property to be retrieved.\n         *\n         * @return the value of the item property, if available, or an empty BitPropVariant.\n         */\n        BIT7Z_NODISCARD BitPropVariant itemProperty( BitProperty property ) const override;\n\n    private:\n        /* Note: a pointer, instead of a reference, allows this class, and hence BitInputArchive::const_iterator,\n         * to be CopyConstructible so that stl algorithms can be used with const_iterator! */\n        const BitInputArchive* mArc;\n\n        BitArchiveItemOffset( uint32_t item_index, const BitInputArchive& item_arc ) noexcept;\n\n        friend class BitInputArchive;\n};\n\n}  // namespace bit7z\n\n#endif // BITARCHIVEITEMOFFSET_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitarchivereader.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITARCHIVEREADER_HPP\n#define BITARCHIVEREADER_HPP\n\n#include \"bitabstractarchiveopener.hpp\"\n#include \"bitarchiveiteminfo.hpp\"\n#include \"bitinputarchive.hpp\"\n\nstruct IInArchive;\nstruct IOutArchive;\nstruct IArchiveExtractCallback;\n\nnamespace bit7z {\n\n/**\n * @brief The BitArchiveReader class allows reading metadata of archives, as well as extracting them.\n */\nclass BitArchiveReader final : public BitAbstractArchiveOpener, public BitInputArchive {\n    public:\n        /**\n         * @brief Constructs a BitArchiveReader object, opening the input file archive.\n         *\n         * @note When bit7z is compiled using the `BIT7Z_AUTO_FORMAT` option, the format\n         * argument has default value BitFormat::Auto (automatic format detection of the input archive).\n         * On the contrary, when `BIT7Z_AUTO_FORMAT` is not defined (i.e., no auto format detection available),\n         * the format argument must be specified.\n         *\n         * @param lib           the 7z library used.\n         * @param in_archive    the path to the archive to be read.\n         * @param format        the format of the input archive.\n         * @param password      the password needed for opening the input archive.\n         */\n        BitArchiveReader( const Bit7zLibrary& lib,\n                          const tstring& in_archive,\n                          const BitInFormat& format BIT7Z_DEFAULT_FORMAT,\n                          const tstring& password = {} );\n\n        /**\n         * @brief Constructs a BitArchiveReader object, opening the archive in the input buffer.\n         *\n         * @note When bit7z is compiled using the `BIT7Z_AUTO_FORMAT` option, the format\n         * argument has default value BitFormat::Auto (automatic format detection of the input archive).\n         * On the contrary, when `BIT7Z_AUTO_FORMAT` is not defined (i.e., no auto format detection available),\n         * the format argument must be specified.\n         *\n         * @param lib           the 7z library used.\n         * @param in_archive    the input buffer containing the archive to be read.\n         * @param format        the format of the input archive.\n         * @param password      the password needed for opening the input archive.\n         */\n        BitArchiveReader( const Bit7zLibrary& lib,\n                          const std::vector< byte_t >& in_archive,\n                          const BitInFormat& format BIT7Z_DEFAULT_FORMAT,\n                          const tstring& password = {} );\n\n        /**\n         * @brief Constructs a BitArchiveReader object, opening the archive from the standard input stream.\n         *\n         * @note When bit7z is compiled using the `BIT7Z_AUTO_FORMAT` option, the format\n         * argument has default value BitFormat::Auto (automatic format detection of the input archive).\n         * On the contrary, when `BIT7Z_AUTO_FORMAT` is not defined (i.e., no auto format detection available),\n         * the format argument must be specified.\n         *\n         * @param lib           the 7z library used.\n         * @param in_archive    the standard input stream of the archive to be read.\n         * @param format        the format of the input archive.\n         * @param password      the password needed for opening the input archive.\n         */\n        BitArchiveReader( const Bit7zLibrary& lib,\n                          std::istream& in_archive,\n                          const BitInFormat& format BIT7Z_DEFAULT_FORMAT,\n                          const tstring& password = {} );\n\n        BitArchiveReader( const BitArchiveReader& ) = delete;\n\n        BitArchiveReader( BitArchiveReader&& ) = delete;\n\n        BitArchiveReader& operator=( const BitArchiveReader& ) = delete;\n\n        BitArchiveReader& operator=( BitArchiveReader&& ) = delete;\n\n        /**\n         * @brief BitArchiveReader destructor.\n         *\n         * @note It releases the input archive file.\n         */\n        ~BitArchiveReader() override = default;\n\n        /**\n         * @return a map of all the available (i.e., non-empty) archive properties and their respective values.\n         */\n        BIT7Z_NODISCARD map< BitProperty, BitPropVariant > archiveProperties() const;\n\n        /**\n         * @return a vector of all the archive items as BitArchiveItem objects.\n         */\n        BIT7Z_NODISCARD vector< BitArchiveItemInfo > items() const;\n\n        /**\n         * @return the number of folders contained in the archive.\n         */\n        BIT7Z_NODISCARD uint32_t foldersCount() const;\n\n        /**\n         * @return the number of files contained in the archive.\n         */\n        BIT7Z_NODISCARD uint32_t filesCount() const;\n\n        /**\n         * @return the total uncompressed size of the archive content.\n         */\n        BIT7Z_NODISCARD uint64_t size() const;\n\n        /**\n         * @return the total compressed size of the archive content.\n         */\n        BIT7Z_NODISCARD uint64_t packSize() const;\n\n        /**\n         * @return true if and only if the archive has at least one encrypted item.\n         */\n        BIT7Z_NODISCARD bool hasEncryptedItems() const;\n\n        /**\n         * @return the number of volumes composing the archive.\n         */\n        BIT7Z_NODISCARD uint32_t volumesCount() const;\n\n        /**\n         * @return true if and only if the archive is composed by multiple volumes.\n         */\n        BIT7Z_NODISCARD bool isMultiVolume() const;\n\n        /**\n         * @return true if and only if the archive was created using solid compression.\n         */\n        BIT7Z_NODISCARD bool isSolid() const;\n};\n\nusing BitArchiveInfo BIT7Z_MAYBE_UNUSED BIT7Z_DEPRECATED_MSG(\"Since v4.0; please use BitArchiveReader.\") = BitArchiveReader;\n\n}  // namespace bit7z\n\n#endif // BITARCHIVEREADER_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitarchivewriter.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITARCHIVEWRITER_HPP\n#define BITARCHIVEWRITER_HPP\n\n#include \"bitoutputarchive.hpp\"\n\nnamespace bit7z {\n\n/**\n * @brief The BitArchiveWriter class allows creating new archives or updating old ones with new items.\n */\nclass BitArchiveWriter : public BitAbstractArchiveCreator, public BitOutputArchive {\n    public:\n        /**\n         * @brief Constructs an empty BitArchiveWriter object that can write archives of the specified format.\n         *\n         * @param lib    the 7z library to use.\n         * @param format the output archive format.\n         */\n        BitArchiveWriter( const Bit7zLibrary& lib, const BitInOutFormat& format );\n\n        /**\n         * @brief Constructs a BitArchiveWriter object, reading the given archive file path.\n         *\n         * @param lib           the 7z library to use.\n         * @param in_archive    the path to an input archive file.\n         * @param format        the input/output archive format.\n         * @param password      (optional) the password needed to read the input archive.\n         */\n        BitArchiveWriter( const Bit7zLibrary& lib,\n                          const tstring& in_archive,\n                          const BitInOutFormat& format,\n                          const tstring& password = {} );\n\n        /**\n         * @brief Constructs a BitArchiveWriter object, reading the archive in the given buffer.\n         *\n         * @param lib           the 7z library to use.\n         * @param in_archive    the buffer containing the input archive.\n         * @param format        the input/output archive format.\n         * @param password      (optional) the password needed to read the input archive.\n         */\n        BitArchiveWriter( const Bit7zLibrary& lib,\n                          const std::vector< byte_t >& in_archive,\n                          const BitInOutFormat& format,\n                          const tstring& password = {} );\n\n        /**\n         * @brief Constructs a BitArchiveWriter object, reading the archive from the given standard input stream.\n         *\n         * @param lib           the 7z library to use.\n         * @param in_archive    the standard stream of the input archive.\n         * @param format        the input/output archive format.\n         * @param password      (optional) the password needed to read the input archive.\n         */\n        BitArchiveWriter( const Bit7zLibrary& lib,\n                          std::istream& in_archive,\n                          const BitInOutFormat& format,\n                          const tstring& password = {} );\n};\n\n}  // namespace bit7z\n\n#endif //BITARCHIVEWRITER_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitcompressionlevel.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITCOMPRESSIONLEVEL_HPP\n#define BITCOMPRESSIONLEVEL_HPP\n\nnamespace bit7z {\n\n/**\n * @brief The BitCompressionLevel enum represents the compression level used by 7z when creating archives.\n * @note It uses the same values as used by [7-zip](https://sevenzip.osdn.jp/chm/cmdline/switches/method.htm#ZipX).\n */\nenum struct BitCompressionLevel {\n    None = 0,    ///< Copy mode (no compression)\n    Fastest = 1, ///< Fastest compressing\n    Fast = 3,    ///< Fast compressing\n    Normal = 5,  ///< Normal compressing\n    Max = 7,     ///< Maximum compressing\n    Ultra = 9    ///< Ultra compressing\n};\n\n}  // namespace bit7z\n\n#endif // BITCOMPRESSIONLEVEL_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitcompressionmethod.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITCOMPRESSIONMETHOD_HPP\n#define BITCOMPRESSIONMETHOD_HPP\n\nnamespace bit7z {\n\n/**\n * @brief The BitCompressionMethod enum represents the compression methods used by 7z when creating archives.\n */\nenum struct BitCompressionMethod {\n    Copy,\n    Deflate,\n    Deflate64,\n    BZip2,\n    Lzma,\n    Lzma2,\n    Ppmd\n};\n\n}  // namespace bit7z\n\n#endif // BITCOMPRESSIONMETHOD_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitcompressor.hpp",
    "content": "// This is an open source non-commercial project. Dear PVS-Studio, please check it.\n// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com\n\n/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITCOMPRESSOR_HPP\n#define BITCOMPRESSOR_HPP\n\n#include <vector>\n\n#include \"bitoutputarchive.hpp\"\n\nnamespace bit7z {\n\nusing std::vector;\n\nnamespace filesystem { // NOLINT(modernize-concat-nested-namespaces)\nnamespace fsutil {\ntstring basename( const tstring& path );\n} // namespace fsutil\n} // namespace filesystem\n\nusing namespace filesystem;\n\n/**\n * @brief The BitCompressor template class allows compressing files into archives.\n *\n * It let decide various properties of the produced archive file, such as the password\n * protection and the compression level desired.\n */\ntemplate< typename Input >\nclass BitCompressor : public BitAbstractArchiveCreator {\n    public:\n        /**\n         * @brief Constructs a BitCompressor object.\n         *\n         * The Bit7zLibrary parameter is needed to have access to the functionalities\n         * of the 7z DLLs. On the contrary, the BitInOutFormat is required to know the\n         * format of the output archive.\n         *\n         * @param lib       the 7z library to use.\n         * @param format    the output archive format.\n         */\n        BitCompressor( Bit7zLibrary const& lib, BitInOutFormat const& format )\n            : BitAbstractArchiveCreator( lib, format ) {}\n\n        /**\n         * @brief Compresses a single file.\n         *\n         * @param in_file       the file to be compressed.\n         * @param out_file      the path (relative or absolute) to the output archive file.\n         * @param input_name    (optional) the name to give to the compressed file inside the output archive.\n         */\n        void compressFile( Input in_file,\n                           const tstring& out_file,\n                           const tstring& input_name = {} ) const {\n            /* Note: if in_file is a filesystem path (i.e., its type is const tstring&), we can deduce the archived\n             * item filename using the original filename. Otherwise, if the user didn't specify the input file name,\n             * we use the filename (without extension) of the output file path. */\n            tstring name;\n#ifdef __cpp_if_constexpr\n            if constexpr ( !std::is_same_v< Input, const tstring& > ) {\n#else\n            //There's probably some compile-time SFINAE alternative for C++14, but life is too short ;)\n            if ( !std::is_same< Input, const tstring& >::value ) {\n#endif\n                name = input_name.empty() ? fsutil::basename( out_file ) : input_name;\n            }\n\n            BitOutputArchive output_archive{ *this, out_file };\n            output_archive.addFile( in_file, name );\n            output_archive.compressTo( out_file );\n        }\n\n        /**\n         * @brief Compresses the input file to the output buffer.\n         *\n         * @param in_file     the file to be compressed.\n         * @param out_buffer  the buffer going to contain the output archive.\n         * @param input_name  (optional) the name to give to the compressed file inside the output archive.\n         */\n        void compressFile( Input in_file,\n                           vector< byte_t >& out_buffer,\n                           const tstring& input_name = {} ) const {\n            BitOutputArchive output_archive{ *this, out_buffer };\n            output_archive.addFile( in_file, input_name );\n            output_archive.compressTo( out_buffer );\n        }\n\n        /**\n         * @brief Compresses the input file to the output stream.\n         *\n         * @param in_file     the file to be compressed.\n         * @param out_stream  the output stream.\n         * @param input_name  (optional) the name to give to the compressed file inside the output archive.\n         */\n        void compressFile( Input in_file,\n                           ostream& out_stream,\n                           const tstring& input_name = {} ) const {\n            BitOutputArchive output_archive{ *this };\n            output_archive.addFile( in_file, input_name );\n            output_archive.compressTo( out_stream );\n        }\n};\n\n}  // namespace bit7z\n\n#endif //BITCOMPRESSOR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitdefines.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITDEFINES_HPP\n#define BITDEFINES_HPP\n\n/* Uncomment the following macros if you don't want to define them yourself in your project files */\n//#define BIT7Z_AUTO_FORMAT\n//#define BIT7Z_REGEX_MATCHING\n//#define BIT7Z_USE_STD_BYTE\n//#define BIT7Z_USE_NATIVE_STRING\n\n#if ( defined( _MSVC_LANG ) && _MSVC_LANG >= 201703L ) || ( defined( __cplusplus ) && __cplusplus >= 201703L )\n#   define BIT7Z_CPP_STANDARD 17\n#elif ( defined( _MSVC_LANG ) && _MSVC_LANG >= 201402L ) || ( defined( __cplusplus ) && __cplusplus >= 201402L )\n#   define BIT7Z_CPP_STANDARD 14\n#else\n#   define BIT7Z_CPP_STANDARD 11\n#endif\n\n#if defined( __cpp_lib_filesystem )\n#   define BIT7Z_USE_STANDARD_FILESYSTEM\n#elif BIT7Z_CPP_STANDARD >= 17 && defined( __has_include )\n#   if __has_include( <filesystem> )\n#       define BIT7Z_USE_STANDARD_FILESYSTEM\n#   endif\n#endif\n\n/* Macro defines for [[nodiscard]] and [[maybe_unused]] attributes. */\n#if defined( __has_cpp_attribute )\n#   if __has_cpp_attribute( nodiscard )\n#       define BIT7Z_NODISCARD [[nodiscard]]\n#   endif\n#   if __has_cpp_attribute( maybe_unused )\n#       define BIT7Z_MAYBE_UNUSED [[maybe_unused]]\n#   endif\n#   if __has_cpp_attribute( deprecated )\n#       define BIT7Z_DEPRECATED [[deprecated]]\n#       define BIT7Z_DEPRECATED_MSG( msg ) [[deprecated( msg )]]\n#   endif\n#endif\n\n/* The compiler doesn't support __has_cpp_attribute, but it is using the C++17 standard. */\n#if !defined( BIT7Z_NODISCARD ) && BIT7Z_CPP_STANDARD >= 17\n#   define BIT7Z_NODISCARD [[nodiscard]]\n#endif\n\n#if !defined( BIT7Z_MAYBE_UNUSED ) && BIT7Z_CPP_STANDARD >= 17\n#   define BIT7Z_MAYBE_UNUSED [[maybe_unused]]\n#endif\n\n#if !defined( BIT7Z_DEPRECATED ) && BIT7Z_CPP_STANDARD >= 14\n#   define BIT7Z_DEPRECATED [[deprecated]]\n#   define BIT7Z_DEPRECATED_MSG( msg ) [[deprecated( msg )]]\n#endif\n\n/* Compiler is using at most the C++14 standard, so we use the compiler-specific attributes/defines were possible. */\n#ifndef BIT7Z_NODISCARD\n#   if defined( __GNUC__ ) || defined(__clang__)\n#       define BIT7Z_NODISCARD __attribute__(( warn_unused_result ))\n#   elif defined( _Check_return_ ) // Old MSVC versions\n#       define BIT7Z_NODISCARD _Check_return_\n#   else\n#       define BIT7Z_NODISCARD\n#   endif\n#endif\n#ifndef BIT7Z_MAYBE_UNUSED\n#   if defined( __GNUC__ ) || defined(__clang__)\n#       define BIT7Z_MAYBE_UNUSED __attribute__(( unused ))\n#   else\n#       define BIT7Z_MAYBE_UNUSED\n#   endif\n#endif\n\n/* Compiler is using the C++11 standard, so we use the compiler-specific attributes were possible.\n * Note: these macros are used in the public API, so we cannot assume that we are always using a C++14 compiler.*/\n#ifndef BIT7Z_DEPRECATED\n#   if defined( __GNUC__ ) || defined( __clang__ )\n#       define BIT7Z_DEPRECATED __attribute__(( __deprecated__ ))\n#       define BIT7Z_DEPRECATED_MSG( msg ) __attribute__(( __deprecated__( msg ) ))\n#   elif defined( _MSC_VER )\n#       define BIT7Z_DEPRECATED __declspec( deprecated )\n#       define BIT7Z_DEPRECATED_MSG( msg ) __declspec( deprecated( msg ) )\n#   else\n#       define BIT7Z_DEPRECATED\n#       define BIT7Z_DEPRECATED_MSG( msg )\n#   endif\n#endif\n\n#ifndef BIT7Z_DEPRECATED_ENUMERATOR\n// Before v6.0, GCC didn't support deprecating single enumerators.\n#   if defined( __GNUC__ ) && !defined( __clang__ ) && __GNUC__ < 6\n#       define BIT7Z_DEPRECATED_ENUMERATOR( deprecated_value, new_value, msg ) deprecated_value = new_value\n#   else\n#       define BIT7Z_DEPRECATED_ENUMERATOR( deprecated_value, new_value, msg ) \\\n                deprecated_value BIT7Z_DEPRECATED_MSG( msg ) = new_value\n#   endif\n#endif\n\n#endif //BITDEFINES_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/biterror.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITERROR_HPP\n#define BITERROR_HPP\n\n#include <system_error>\n\n#include \"bitdefines.hpp\"\n\nnamespace bit7z {\n\n/**\n * @brief The BitError enum struct values represent bit7z specific errors.\n */\nenum struct BitError {\n    Fail = 1,\n    FilterNotSpecified,\n    FormatFeatureNotSupported,\n    IndicesNotSpecified,\n    InvalidArchivePath,\n    InvalidOutputBufferSize,\n    InvalidCompressionMethod,\n    InvalidDictionarySize,\n    InvalidIndex,\n    InvalidWordSize,\n    ItemIsAFolder,\n    ItemMarkedAsDeleted,\n    NoMatchingExtension,\n    NoMatchingItems,\n    NoMatchingSignature,\n    NonEmptyOutputBuffer,\n    RequestedWrongVariantType,\n    UnsupportedOperation,\n    WrongUpdateMode\n};\n\nstd::error_code make_error_code( const BitError& e );\n\n}  // namespace bit7z\n\nnamespace std {\n\ntemplate<>\nstruct BIT7Z_MAYBE_UNUSED is_error_code_enum< bit7z::BitError > : public true_type {};\n\n} // namespace std\n\n\n#endif //BITERROR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitexception.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITEXCEPTION_HPP\n#define BITEXCEPTION_HPP\n\n#include <vector>\n#include <system_error>\n\n#include \"bitdefines.hpp\"\n#include \"bittypes.hpp\"\n#include \"bitwindows.hpp\"\n\nnamespace bit7z {\n\nusing std::system_error;\nusing FailedFiles = std::vector< std::pair< tstring, std::error_code > >;\n\nstd::error_code make_hresult_code( HRESULT res ) noexcept;\n\nstd::error_code last_error_code() noexcept;\n\n/**\n * @brief The BitException class represents a generic exception thrown from the bit7z classes.\n */\nclass BitException final : public system_error {\n    public:\n#ifdef _WIN32\n        using native_code_type = HRESULT;\n#else\n        using native_code_type = int;\n#endif\n\n        /**\n         * @brief Constructs a BitException object with the given message, and the specific files that failed.\n         *\n         * @param message   the message associated with the exception object.\n         * @param files     the vector of files that failed, with the corresponding error codes.\n         * @param code      the HRESULT code associated with the exception object.\n         */\n        explicit BitException( const char* message, std::error_code code, FailedFiles&& files = {} );\n\n        /**\n         * @brief Constructs a BitException object with the given message, and the specific file that failed.\n         *\n         * @param message   the message associated with the exception object.\n         * @param file      the file that failed during the operation.\n         * @param code      the HRESULT code associated with the exception object.\n         */\n        BitException( const char* message, std::error_code code, const tstring& file );\n\n        /**\n         * @brief Constructs a BitException object with the given message.\n         *\n         * @param message   the message associated with the exception object.\n         * @param code      the HRESULT code associated with the exception object.\n         */\n        explicit BitException( const std::string& message, std::error_code code );\n\n        /**\n         * @return the native error code (e.g., HRESULT on Windows, int elsewhere)\n         * corresponding to the exception's std::error_code.\n         */\n        BIT7Z_NODISCARD native_code_type nativeCode() const noexcept;\n\n        /**\n         * @return the HRESULT error code corresponding to the exception's std::error_code.\n         */\n        BIT7Z_NODISCARD HRESULT hresultCode() const noexcept;\n\n        /**\n         * @return the POSIX error code corresponding to the exception's std::error_code.\n         */\n        BIT7Z_NODISCARD int posixCode() const noexcept;\n\n        /**\n         * @return the vector of files that caused the exception to be thrown, along with the corresponding\n         *         error codes.\n         */\n        BIT7Z_NODISCARD const FailedFiles& failedFiles() const noexcept;\n\n    private:\n        FailedFiles mFailedFiles;\n};\n\n}  // namespace bit7z\n\n#endif // BITEXCEPTION_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitextractor.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITEXTRACTOR_HPP\n#define BITEXTRACTOR_HPP\n\n#include <algorithm>\n\n#include \"bitabstractarchiveopener.hpp\"\n#include \"biterror.hpp\"\n#include \"bitexception.hpp\"\n#include \"bitinputarchive.hpp\"\n\nnamespace bit7z {\n\nnamespace filesystem { // NOLINT(modernize-concat-nested-namespaces)\nnamespace fsutil {\nbool wildcardMatch( const tstring& pattern, const tstring& str );\n} // namespace fsutil\n} // namespace filesystem\n\n/**\n * @brief Enumeration representing the policy according to which the extractor should handle\n * the items that match the pattern given by the user.\n */\nenum struct FilterPolicy {\n    Include, ///< Extract the items that match the pattern.\n    Exclude  ///< Do not extract the items that match the pattern.\n};\n\n/**\n * @brief The BitExtractor template class allows extracting the content of archives from supported input types.\n *\n * @tparam Input    the type of input archives that the generated extractor class supports.\n */\ntemplate< typename Input >\nclass BitExtractor final : public BitAbstractArchiveOpener {\n    public:\n        /**\n         * @brief Constructs a BitExtractor object.\n         *\n         * The Bit7zLibrary parameter is needed to have access to the functionalities\n         * of the 7z DLLs. On the contrary, the BitInFormat is required to know the\n         * format of the in_file archives.\n         *\n         * @note When bit7z is compiled using the BIT7Z_AUTO_FORMAT macro define, the format\n         * argument has default value BitFormat::Auto (automatic format detection of the in_file archive).\n         * Otherwise, when BIT7Z_AUTO_FORMAT is not defined (i.e., no auto format detection available),\n         * the format argument must be specified.\n         *\n         * @param lib       the 7z library to use.\n         * @param format    the in_file archive format.\n         */\n        explicit BitExtractor( const Bit7zLibrary& lib, const BitInFormat& format BIT7Z_DEFAULT_FORMAT )\n            : BitAbstractArchiveOpener( lib, format ) {}\n\n        /**\n         * @brief Extracts the given archive to the chosen directory.\n         *\n         * @param in_archive    the input archive to be extracted.\n         * @param out_dir       the output directory where extracted files will be put.\n         */\n        void extract( Input in_archive, const tstring& out_dir = {} ) const {\n            BitInputArchive input_archive( *this, in_archive );\n            input_archive.extract( out_dir );\n        }\n\n        /**\n         * @brief Extracts a file from the given archive to the output buffer.\n         *\n         * @param in_archive   the input archive to extract from.\n         * @param out_buffer   the output buffer where the content of the extracted file will be put.\n         * @param index        the index of the file to be extracted from the archive.\n         */\n        void extract( Input in_archive, vector< byte_t >& out_buffer, uint32_t index = 0 ) const {\n            BitInputArchive input_archive( *this, in_archive );\n            input_archive.extract( out_buffer, index );\n        }\n\n        /**\n         * @brief Extracts a file from the given archive to the output stream.\n         *\n         * @param in_archive   the input archive to extract from.\n         * @param out_stream   the (binary) stream where the content of the extracted file will be put.\n         * @param index        the index of the file to be extracted from the archive.\n         */\n        void extract( Input in_archive, std::ostream& out_stream, uint32_t index = 0 ) const {\n            BitInputArchive input_archive( *this, in_archive );\n            input_archive.extract( out_stream, index );\n        }\n\n        /**\n         * @brief Extracts the content of the given archive into a map of memory buffers, where the keys are\n         * the paths of the files (inside the archive), and the values are their decompressed contents.\n         *\n         * @param in_archive    the input archive to be extracted.\n         * @param out_map       the output map.\n         */\n        void extract( Input in_archive, std::map< tstring, vector< byte_t > >& out_map ) const {\n            BitInputArchive input_archive( *this, in_archive );\n            input_archive.extract( out_map );\n        }\n\n        /**\n         * @brief Extracts the files in the archive that match the given wildcard pattern to the chosen directory.\n         *\n         * @param in_archive    the input archive to extract from.\n         * @param item_filter   the wildcard pattern used for matching the paths of files inside the archive.\n         * @param out_dir       the output directory where extracted files will be put.\n         * @param policy        the filtering policy to be applied to the matched items.\n         */\n        void extractMatching( Input in_archive,\n                              const tstring& item_filter,\n                              const tstring& out_dir = {},\n                              FilterPolicy policy = FilterPolicy::Include ) const {\n            using namespace filesystem;\n\n            if ( item_filter.empty() ) {\n                throw BitException( \"Cannot extract items\", make_error_code( BitError::FilterNotSpecified ) );\n            }\n\n            extractMatchingFilter( in_archive, out_dir, policy, [ &item_filter ]( const tstring& item_path ) -> bool {\n                return fsutil::wildcardMatch( item_filter, item_path );\n            } );\n        }\n\n        /**\n         * @brief Extracts to the output buffer the first file in the archive matching the given wildcard pattern.\n         *\n         * @param in_archive    the input archive to extract from.\n         * @param item_filter   the wildcard pattern used for matching the paths of files inside the archive.\n         * @param out_buffer    the output buffer where to extract the file.\n         * @param policy        the filtering policy to be applied to the matched items.\n         */\n        void extractMatching( Input in_archive,\n                              const tstring& item_filter,\n                              vector< byte_t >& out_buffer,\n                              FilterPolicy policy = FilterPolicy::Include ) const {\n            using namespace filesystem;\n\n            if ( item_filter.empty() ) {\n                throw BitException( \"Cannot extract items\", make_error_code( BitError::FilterNotSpecified ) );\n            }\n\n            extractMatchingFilter( in_archive, out_buffer, policy,\n                                   [ &item_filter ]( const tstring& item_path ) -> bool {\n                                       return fsutil::wildcardMatch( item_filter, item_path );\n                                   } );\n        }\n\n        /**\n         * @brief Extracts the specified items from the given archive to the chosen directory.\n         *\n         * @param in_archive    the input archive to extract from.\n         * @param indices       the indices of the files in the archive that should be extracted.\n         * @param out_dir       the output directory where the extracted files will be placed.\n         */\n        void extractItems( Input in_archive,\n                           const std::vector< uint32_t >& indices,\n                           const tstring& out_dir = {} ) const {\n            if ( indices.empty() ) {\n                throw BitException( \"Cannot extract items\", make_error_code( BitError::IndicesNotSpecified ) );\n            }\n\n            BitInputArchive input_archive( *this, in_archive );\n            uint32_t n_items = input_archive.itemsCount();\n            // Find if any index passed by the user is not in the valid range [0, itemsCount() - 1]\n            const auto find_res = std::find_if( indices.cbegin(),\n                                                indices.cend(),\n                                                [ &n_items ]( uint32_t index ) -> bool {\n                                                    return index >= n_items;\n                                                } );\n            if ( find_res != indices.cend() ) {\n                throw BitException( \"Cannot extract item at the index \" + std::to_string( *find_res ),\n                                    make_error_code( BitError::InvalidIndex ) );\n            }\n\n            input_archive.extract( out_dir, indices );\n        }\n\n#ifdef BIT7Z_REGEX_MATCHING\n\n        /**\n         * @brief Extracts the files in the archive that match the given regex pattern to the chosen directory.\n         *\n         * @note Available only when compiling bit7z using the BIT7Z_REGEX_MATCHING preprocessor define.\n         *\n         * @param in_archive    the input archive to extract from.\n         * @param regex         the regex used for matching the paths of files inside the archive.\n         * @param out_dir       the output directory where extracted files will be put.\n         * @param policy        the filtering policy to be applied to the matched items.\n         */\n        void extractMatchingRegex( Input in_archive,\n                                   const tstring& regex,\n                                   const tstring& out_dir = {},\n                                   FilterPolicy policy = FilterPolicy::Include ) const {\n            if ( regex.empty() ) {\n                throw BitException( \"Cannot extract items\", make_error_code( BitError::FilterNotSpecified ) );\n            }\n\n            const tregex regex_filter( regex, tregex::ECMAScript | tregex::optimize );\n            extractMatchingFilter( in_archive, out_dir, policy, [ &regex_filter ]( const tstring& item_path ) -> bool {\n                return std::regex_match( item_path, regex_filter );\n            } );\n        }\n\n        /**\n         * @brief Extracts the first file in the archive that matches the given regex pattern to the output buffer.\n         *\n         * @note Available only when compiling bit7z using the BIT7Z_REGEX_MATCHING preprocessor define.\n         *\n         * @param in_archive    the input archive to extract from.\n         * @param regex         the regex used for matching the paths of files inside the archive.\n         * @param out_buffer    the output buffer where the extracted file will be put.\n         * @param policy        the filtering policy to be applied to the matched items.\n         */\n        void extractMatchingRegex( Input in_archive,\n                                   const tstring& regex,\n                                   vector< byte_t >& out_buffer,\n                                   FilterPolicy policy = FilterPolicy::Include ) const {\n            if ( regex.empty() ) {\n                throw BitException( \"Cannot extract items\", make_error_code( BitError::FilterNotSpecified ) );\n            }\n\n            const tregex regex_filter( regex, tregex::ECMAScript | tregex::optimize );\n            return extractMatchingFilter( in_archive, out_buffer, policy,\n                                          [ &regex_filter ]( const tstring& item_path ) -> bool {\n                                              return std::regex_match( item_path, regex_filter );\n                                          } );\n        }\n\n#endif\n\n        /**\n         * @brief Tests the given archive without extracting its content.\n         *\n         * If the archive is not valid, a BitException is thrown!\n         *\n         * @param in_archive   the input archive to be tested.\n         */\n        void test( Input in_archive ) const {\n            BitInputArchive input_archive( *this, in_archive );\n            input_archive.test();\n        }\n\n    private:\n        void extractMatchingFilter( Input in_archive,\n                                    const tstring& out_dir,\n                                    FilterPolicy policy,\n                                    const function< bool( const tstring& ) >& filter ) const {\n            BitInputArchive input_archive( *this, in_archive );\n\n            vector< uint32_t > matched_indices;\n            const bool should_extract_matched_items = policy == FilterPolicy::Include;\n            // Searching for files inside the archive that match the given filter\n            for ( const auto& item : input_archive ) {\n                const bool item_matches = filter( item.path() );\n                if ( item_matches == should_extract_matched_items ) {\n                    /* The if-condition is equivalent to an exclusive NOR (negated XOR) between\n                     * item_matches and should_extract_matched_items.\n                     * In other words, it is true only if the current item either:\n                     *  - matches the filter, and we must include any matching item; or\n                     *  - doesn't match the filter, and we must exclude those that match. */\n                    matched_indices.push_back( item.index() );\n                }\n            }\n\n            if ( matched_indices.empty() ) {\n                throw BitException( \"Cannot extract items\", make_error_code( BitError::NoMatchingItems ) );\n            }\n\n            input_archive.extract( out_dir, matched_indices );\n        }\n\n        void extractMatchingFilter( Input in_archive,\n                                    vector< byte_t >& out_buffer,\n                                    FilterPolicy policy,\n                                    const function< bool( const tstring& ) >& filter ) const {\n            BitInputArchive input_archive( *this, in_archive );\n\n            const bool should_extract_matched_item = policy == FilterPolicy::Include;\n            // Searching for files inside the archive that match the given filter\n            for ( const auto& item : input_archive ) {\n                const bool item_matches = filter( item.path() );\n                if ( item_matches == should_extract_matched_item ) {\n                    /* The if-condition is equivalent to an exclusive NOR (negated XOR) between\n                     *  item_matches and should_extract_matched_item. */\n                    input_archive.extract( out_buffer, item.index() );\n                    return;\n                }\n            }\n\n            throw BitException( \"Failed to extract items\", make_error_code( BitError::NoMatchingItems ) );\n        }\n};\n\n}  // namespace bit7z\n\n#endif //BITEXTRACTOR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitfilecompressor.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITFILECOMPRESSOR_HPP\n#define BITFILECOMPRESSOR_HPP\n\n#include <map>\n#include <ostream>\n#include <vector>\n\n#include \"bitcompressor.hpp\"\n\nnamespace bit7z {\n\nusing std::vector;\nusing std::map;\nusing std::ostream;\n\nnamespace filesystem {\nclass FSItem;\n} // namespace filesystem\n\nusing namespace filesystem;\n\n/**\n * @brief The BitFileCompressor class allows compressing files and directories.\n * The compressed archives can be saved to the filesystem, standard streams, or memory buffers.\n *\n * It let decide various properties of the produced archive, such as the password\n * protection and the compression level desired.\n */\nclass BitFileCompressor final : public BitCompressor< const tstring& > {\n    public:\n        /**\n         * @brief Constructs a BitFileCompressor object.\n         *\n         * The Bit7zLibrary parameter is needed to have access to the functionalities\n         * of the 7z DLLs. On the contrary, the BitInOutFormat is required to know the\n         * format of the output archive.\n         *\n         * @param lib       the 7z library used.\n         * @param format    the output archive format.\n         */\n        BitFileCompressor( const Bit7zLibrary& lib, const BitInOutFormat& format );\n\n        /* Compression from the file system to the file system. */\n\n        /**\n         * @brief Compresses the given files or directories.\n         *\n         * The items in the first argument must be the relative or absolute paths to files or\n         * directories existing on the filesystem.\n         *\n         * @param in_paths  a vector of paths.\n         * @param out_file  the path (relative or absolute) to the output archive file.\n         */\n        void compress( const std::vector< tstring >& in_paths, const tstring& out_file ) const;\n\n        /**\n         * @brief Compresses the given files or directories using the specified aliases.\n         *\n         * The items in the first argument must be the relative or absolute paths to files or\n         * directories existing on the filesystem.\n         * Each pair in the map must follow the following format:\n         *  {\"path to file in the filesystem\", \"alias path in the archive\"}.\n         *\n         * @param in_paths  a map of paths and corresponding aliases.\n         * @param out_file  the path (relative or absolute) to the output archive file.\n         */\n        void compress( const std::map< tstring, tstring >& in_paths, const tstring& out_file ) const;\n\n        /**\n         * @brief Compresses a group of files.\n         *\n         * @note Any path to a directory or to a not-existing file will be ignored!\n         *\n         * @param in_files  the path (relative or absolute) to the input files.\n         * @param out_file  the path (relative or absolute) to the output archive file.\n         */\n        void compressFiles( const std::vector< tstring >& in_files, const tstring& out_file ) const;\n\n        /**\n         * @brief Compresses the files contained in a directory.\n         *\n         * @param in_dir        the path (relative or absolute) to the input directory.\n         * @param out_file      the path (relative or absolute) to the output archive file.\n         * @param recursive     (optional) if true, it searches files inside the sub-folders of in_dir.\n         * @param filter        (optional) the filter to use when searching files inside in_dir.\n         */\n        void compressFiles( const tstring& in_dir,\n                            const tstring& out_file,\n                            bool recursive = true,\n                            const tstring& filter = BIT7Z_STRING( \"*\" ) ) const;\n\n        /**\n         * @brief Compresses an entire directory.\n         *\n         * @note This method is equivalent to compressFiles with filter set to L\"\".\n         *\n         * @param in_dir    the path (relative or absolute) to the input directory.\n         * @param out_file  the path (relative or absolute) to the output archive file.\n         */\n        void compressDirectory( const tstring& in_dir, const tstring& out_file ) const;\n\n        /* Compression from the file system to standard streams. */\n\n        /**\n         * @brief Compresses the given files or directories.\n         *\n         * The items in the first argument must be the relative or absolute paths to files or\n         * directories existing on the filesystem.\n         *\n         * @param in_paths      a vector of paths.\n         * @param out_stream    the standard ostream where the archive will be output.\n         */\n        void compress( const std::vector< tstring >& in_paths, std::ostream& out_stream ) const;\n\n        /**\n         * @brief Compresses the given files or directories using the specified aliases.\n         *\n         * The items in the first argument must be the relative or absolute paths to files or\n         * directories existing on the filesystem.\n         * Each pair in the map must follow the following format:\n         *  {\"path to file in the filesystem\", \"alias path in the archive\"}.\n         *\n         * @param in_paths      a map of paths and corresponding aliases.\n         * @param out_stream    the standard ostream where to output the archive file.\n         */\n        void compress( const std::map< tstring, tstring >& in_paths, std::ostream& out_stream ) const;\n};\n\n}  // namespace bit7z\n#endif // BITFILECOMPRESSOR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitfileextractor.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITFILEEXTRACTOR_HPP\n#define BITFILEEXTRACTOR_HPP\n\n#include \"bitextractor.hpp\"\n\nnamespace bit7z {\n\n/**\n * @brief The BitFileExtractor alias allows extracting archives on the filesystem.\n */\nusing BitFileExtractor BIT7Z_MAYBE_UNUSED = BitExtractor< const tstring& >;\n\n} // namespace bit7z\n#endif // BITFILEEXTRACTOR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitformat.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITFORMAT_HPP\n#define BITFORMAT_HPP\n\n#include <bitset>\n\n#include <type_traits>\n\n#include \"bitcompressionmethod.hpp\"\n#include \"bitdefines.hpp\"\n#include \"bittypes.hpp\"\n\nnamespace bit7z {\n\n/**\n * @brief The FormatFeatures enum specifies the features supported by an archive file format.\n */\nenum struct FormatFeatures : unsigned {\n    MultipleFiles = 1 << 0,    ///< The format can compress/extract multiple files         (2^0 = 0000001)\n    SolidArchive = 1 << 1,     ///< The format supports solid archives                     (2^1 = 0000010)\n    CompressionLevel = 1 << 2, ///< The format is able to use different compression levels (2^2 = 0000100)\n    Encryption = 1 << 3,       ///< The format supports archive encryption                 (2^3 = 0001000)\n    HeaderEncryption = 1 << 4, ///< The format can encrypt the file names                  (2^4 = 0010000)\n    MultipleMethods = 1 << 5   ///< The format can use different compression methods       (2^6 = 0100000)\n};\n\ntemplate< typename E >\nusing underlying_type_t = typename std::underlying_type< E >::type;\n\ntemplate< typename E >\ninline constexpr auto to_underlying( E e ) noexcept -> underlying_type_t< E > {\n    return static_cast< underlying_type_t< E > >( e );\n}\n\ninline constexpr FormatFeatures operator|( FormatFeatures lhs, FormatFeatures rhs ) noexcept {\n    return static_cast< FormatFeatures >( to_underlying( lhs ) | to_underlying( rhs ) );\n}\n\nusing FormatFeaturesType = underlying_type_t< FormatFeatures >;\n\ninline constexpr auto operator&( FormatFeatures lhs, FormatFeatures rhs ) noexcept -> FormatFeaturesType {\n    return to_underlying( lhs ) & to_underlying( rhs );\n}\n\n/**\n * @brief The BitInFormat class specifies an extractable archive format.\n *\n * @note Usually, the user of the library should not create new formats and, instead,\n * use the ones provided by the BitFormat namespace.\n */\nclass BitInFormat {\n    public:\n        //non-copyable\n        BitInFormat( const BitInFormat& other ) = delete;\n\n        BitInFormat& operator=( const BitInFormat& other ) = delete;\n\n        //non-movable\n        BitInFormat( BitInFormat&& other ) = delete;\n\n        BitInFormat& operator=( BitInFormat&& other ) = delete;\n\n        ~BitInFormat() = default;\n\n        /**\n         * @brief Constructs a BitInFormat object with the ID value used by the 7z SDK.\n         * @param value  the value of the format in the 7z SDK.\n         */\n        constexpr explicit BitInFormat( unsigned char value ) noexcept: mValue( value ) {}\n\n        /**\n         * @return the value of the format in the 7z SDK.\n         */\n        BIT7Z_NODISCARD unsigned char value() const noexcept;\n\n        /**\n         * @param other  the target object to compare to.\n         * @return a boolean value indicating whether this format is equal to the \"other\" or not.\n         */\n        bool operator==( BitInFormat const& other ) const noexcept;\n\n        /**\n         * @param other  the target object to compare to.\n         * @return a boolean value indicating whether this format is different from the \"other\" or not.\n         */\n        bool operator!=( BitInFormat const& other ) const noexcept;\n\n    private:\n        unsigned char mValue;\n};\n\n/**\n * @brief The BitInOutFormat class specifies a format available for creating new archives and extract old ones.\n *\n * @note Usually, the user of the library should not create new formats and, instead,\n * use the ones provided by the BitFormat namespace.\n */\nclass BitInOutFormat final : public BitInFormat {\n    public:\n        /**\n         * @brief Constructs a BitInOutFormat object with an ID value, an extension and a set of supported features.\n         *\n         * @param value         the value of the format in the 7z SDK.\n         * @param ext           the default file extension of the archive format.\n         * @param defaultMethod the default method used for compressing the archive format.\n         * @param features      the set of features supported by the archive format\n         */\n        constexpr BitInOutFormat( unsigned char value,\n                                  const tchar* ext,\n                                  BitCompressionMethod defaultMethod,\n                                  FormatFeatures features ) noexcept\n            : BitInFormat( value ), mExtension( ext ), mDefaultMethod( defaultMethod ), mFeatures( features ) {}\n\n        //non-copyable\n        BitInOutFormat( const BitInOutFormat& other ) = delete;\n\n        BitInOutFormat& operator=( const BitInOutFormat& other ) = delete;\n\n        //non-movable\n        BitInOutFormat( BitInOutFormat&& other ) = delete;\n\n        BitInOutFormat& operator=( BitInOutFormat&& other ) = delete;\n\n        ~BitInOutFormat() = default;\n\n        /**\n         * @return the default file extension of the archive format.\n         */\n        BIT7Z_NODISCARD const tchar* extension() const noexcept;\n\n        /**\n         * @return the bitset of the features supported by the format.\n         */\n        BIT7Z_NODISCARD FormatFeatures features() const noexcept;\n\n        /**\n         * @brief Checks if the format has a specific feature (see FormatFeatures enum).\n         *\n         * @param feature   feature to be checked.\n         *\n         * @return a boolean value indicating whether the format has the given feature.\n         */\n        BIT7Z_NODISCARD bool hasFeature( FormatFeatures feature ) const noexcept;\n\n        /**\n         * @return the default method used for compressing the archive format.\n         */\n        BIT7Z_NODISCARD BitCompressionMethod defaultMethod() const noexcept;\n\n    private:\n        const tchar* mExtension;\n        BitCompressionMethod mDefaultMethod;\n        FormatFeatures mFeatures;\n};\n\n/**\n * @brief The namespace that contains a set of archive formats usable with bit7z classes.\n */\nnamespace BitFormat {\n#ifdef BIT7Z_AUTO_FORMAT\n/**\n * @brief Automatic Format Detection (available only when compiling bit7z using the `BIT7Z_AUTO_FORMAT` option).\n */\nextern const BitInFormat Auto;\n#endif\nextern const BitInFormat Rar,       ///< RAR Archive Format\n                         Arj,       ///< ARJ Archive Format\n                         Z,         ///< Z Archive Format\n                         Lzh,       ///< LZH Archive Format\n                         Cab,       ///< CAB Archive Format\n                         Nsis,      ///< NSIS Archive Format\n                         Lzma,      ///< LZMA Archive Format\n                         Lzma86,    ///< LZMA86 Archive Format\n                         Ppmd,      ///< PPMD Archive Format\n                         Vhdx,      ///< VHDX Archive Format\n                         COFF,      ///< COFF Archive Format\n                         Ext,       ///< EXT Archive Format\n                         VMDK,      ///< VMDK Archive Format\n                         VDI,       ///< VDI Archive Format\n                         QCow,      ///< QCOW Archive Format\n                         GPT,       ///< GPT Archive Format\n                         Rar5,      ///< RAR5 Archive Format\n                         IHex,      ///< IHEX Archive Format\n                         Hxs,       ///< HXS Archive Format\n                         TE,        ///< TE Archive Format\n                         UEFIc,     ///< UEFIc Archive Format\n                         UEFIs,     ///< UEFIs Archive Format\n                         SquashFS,  ///< SquashFS Archive Format\n                         CramFS,    ///< CramFS Archive Format\n                         APM,       ///< APM Archive Format\n                         Mslz,      ///< MSLZ Archive Format\n                         Flv,       ///< FLV Archive Format\n                         Swf,       ///< SWF Archive Format\n                         Swfc,      ///< SWFC Archive Format\n                         Ntfs,      ///< NTFS Archive Format\n                         Fat,       ///< FAT Archive Format\n                         Mbr,       ///< MBR Archive Format\n                         Vhd,       ///< VHD Archive Format\n                         Pe,        ///< PE Archive Format\n                         Elf,       ///< ELF Archive Format\n                         Macho,     ///< MACHO Archive Format\n                         Udf,       ///< UDF Archive Format\n                         Xar,       ///< XAR Archive Format\n                         Mub,       ///< MUB Archive Format\n                         Hfs,       ///< HFS Archive Format\n                         Dmg,       ///< DMG Archive Format\n                         Compound,  ///< COMPOUND Archive Format\n                         Iso,       ///< ISO Archive Format\n                         Chm,       ///< CHM Archive Format\n                         Split,     ///< SPLIT Archive Format\n                         Rpm,       ///< RPM Archive Format\n                         Deb,       ///< DEB Archive Format\n                         Cpio;      ///< CPIO Archive Format\n\nextern const BitInOutFormat Zip,        ///< ZIP Archive Format\n                            BZip2,      ///< BZIP2 Archive Format\n                            SevenZip,   ///< 7Z Archive Format\n                            Xz,         ///< XZ Archive Format\n                            Wim,        ///< WIM Archive Format\n                            Tar,        ///< TAR Archive Format\n                            GZip;       ///< GZIP Archive Format\n}  // namespace BitFormat\n\n\n#ifdef BIT7Z_AUTO_FORMAT\n#define BIT7Z_DEFAULT_FORMAT = BitFormat::Auto\n#else\n#define BIT7Z_DEFAULT_FORMAT\n#endif\n\n}  // namespace bit7z\n\n#endif // BITFORMAT_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitfs.hpp",
    "content": "// This is an open source non-commercial project. Dear PVS-Studio, please check it.\n// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com\n\n/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITFS_HPP\n#define BITFS_HPP\n\n/* Header for forward declaring fs namespace. */\n\n#include \"bitdefines.hpp\" /* For BIT7Z_USE_STANDARD_FILESYSTEM */\n\n#ifdef BIT7Z_USE_STANDARD_FILESYSTEM\n#include <filesystem>\n#else\n/* Notes: we use this forward declaration to avoid including private headers (e.g. fs.hpp).\n *        Since some public API headers include bitgenericitem.hpp (e.g. \"bitoutputarchive.hpp\"),\n *        including private headers here would result in the \"leaking\" out of these latter in the public API.*/\nnamespace ghc {\n    namespace filesystem {\n        class path;\n    }\n}\n#endif\n\nnamespace fs {\n#ifdef BIT7Z_USE_STANDARD_FILESYSTEM\nusing namespace std::filesystem;\n#else\nusing namespace ghc::filesystem;\n#endif\n} // namespace fs\n\n#endif //BITFS_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitgenericitem.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITGENERICITEM_HPP\n#define BITGENERICITEM_HPP\n\n#include \"bitpropvariant.hpp\"\n\nnamespace bit7z {\n\n/**\n * @brief The BitGenericItem interface class represents a generic item (either inside or outside an archive).\n */\nclass BitGenericItem {\n    public:\n        /**\n         * @return true if and only if the item is a directory (i.e., it has the property BitProperty::IsDir).\n         */\n        BIT7Z_NODISCARD virtual bool isDir() const = 0;\n\n        /**\n         * @return the uncompressed size of the item.\n         */\n        BIT7Z_NODISCARD virtual uint64_t size() const = 0;\n\n        /**\n         * @return the name of the item, if available or inferable from the path, or an empty string otherwise.\n         */\n        BIT7Z_NODISCARD virtual tstring name() const = 0;\n\n        /**\n         * @return the path of the item.\n         */\n        BIT7Z_NODISCARD virtual tstring path() const = 0;\n\n        /**\n         * @return the item attributes.\n         */\n        BIT7Z_NODISCARD virtual uint32_t attributes() const = 0;\n\n        /**\n         * @brief Gets the specified item property.\n         *\n         * @param property  the property to be retrieved.\n         *\n         * @return the value of the item property, if available, or an empty BitPropVariant.\n         */\n        BIT7Z_NODISCARD virtual BitPropVariant itemProperty( BitProperty property ) const = 0;\n\n        virtual ~BitGenericItem() = default;\n};\n\n}  // namespace bit7z\n\n#endif //BITGENERICITEM_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitinputarchive.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n#ifndef BITINPUTARCHIVE_HPP\n#define BITINPUTARCHIVE_HPP\n\n#include <array>\n#include <map>\n\n#include \"bitabstractarchivehandler.hpp\"\n#include \"bitarchiveitemoffset.hpp\"\n#include \"bitformat.hpp\"\n#include \"bitfs.hpp\"\n\nstruct IInStream;\nstruct IInArchive;\nstruct IOutArchive;\n\nnamespace bit7z {\n\nusing std::vector;\n\n/**\n * @brief The BitInputArchive class, given a handler object, allows reading/extracting the content of archives.\n */\nclass BitInputArchive {\n    public:\n        /**\n         * @brief Constructs a BitInputArchive object, opening the input file archive.\n         *\n         * @param handler   the reference to the BitAbstractArchiveHandler object containing all the settings to\n         *                  be used for reading the input archive\n         * @param in_file   the path to the input archive file\n         */\n        BitInputArchive( const BitAbstractArchiveHandler& handler, const tstring& in_file );\n\n        /**\n         * @brief Constructs a BitInputArchive object, opening the input file archive.\n         *\n         * @param handler   the reference to the BitAbstractArchiveHandler object containing all the settings to\n         *                  be used for reading the input archive\n         * @param arc_path  the path to the input archive file\n         */\n#if defined( _WIN32 ) && defined( BIT7Z_AUTO_PREFIX_LONG_PATHS )\n        BitInputArchive( const BitAbstractArchiveHandler& handler, fs::path arc_path );\n#else\n        BitInputArchive( const BitAbstractArchiveHandler& handler, const fs::path& arc_path );\n#endif\n\n        /**\n         * @brief Constructs a BitInputArchive object, opening the archive given in the input buffer.\n         *\n         * @param handler   the reference to the BitAbstractArchiveHandler object containing all the settings to\n         *                  be used for reading the input archive\n         * @param in_buffer the buffer containing the input archive\n         */\n        BitInputArchive( const BitAbstractArchiveHandler& handler, const std::vector< byte_t >& in_buffer );\n\n        /**\n         * @brief Constructs a BitInputArchive object, opening the archive by reading the given input stream.\n         *\n         * @param handler   the reference to the BitAbstractArchiveHandler object containing all the settings to\n         *                  be used for reading the input archive\n         * @param in_stream the standard input stream of the input archive\n         */\n        BitInputArchive( const BitAbstractArchiveHandler& handler, std::istream& in_stream );\n\n        BitInputArchive( const BitInputArchive& ) = delete;\n\n        BitInputArchive( BitInputArchive&& ) = delete;\n\n        BitInputArchive& operator=( const BitInputArchive& ) = delete;\n\n        BitInputArchive& operator=( BitInputArchive&& ) = delete;\n\n        virtual ~BitInputArchive();\n\n        /**\n         * @return the detected format of the file.\n         */\n        BIT7Z_NODISCARD const BitInFormat& detectedFormat() const noexcept;\n\n        /**\n         * @brief Gets the specified archive property.\n         *\n         * @param property  the property to be retrieved.\n         *\n         * @return the current value of the archive property or an empty BitPropVariant if no value is specified.\n         */\n        BIT7Z_NODISCARD BitPropVariant archiveProperty( BitProperty property ) const;\n\n        /**\n         * @brief Gets the specified property of an item in the archive.\n         *\n         * @param index     the index (in the archive) of the item.\n         * @param property  the property to be retrieved.\n         *\n         * @return the current value of the item property or an empty BitPropVariant if the item has no value for\n         * the property.\n         */\n        BIT7Z_NODISCARD BitPropVariant itemProperty( uint32_t index, BitProperty property ) const;\n\n        /**\n         * @return the number of items contained in the archive.\n         */\n        BIT7Z_NODISCARD uint32_t itemsCount() const;\n\n        /**\n         * @param index the index of an item in the archive.\n         *\n         * @return true if and only if the item at the given index is a folder.\n         */\n        BIT7Z_NODISCARD bool isItemFolder( uint32_t index ) const;\n\n        /**\n         * @param index the index of an item in the archive.\n         *\n         * @return true if and only if the item at the given index is encrypted.\n         */\n        BIT7Z_NODISCARD bool isItemEncrypted( uint32_t index ) const;\n\n        /**\n         * @return the path to the archive (the empty string for buffer/stream archives).\n         */\n        BIT7Z_NODISCARD const tstring& archivePath() const noexcept;\n\n        /**\n         * @return the BitAbstractArchiveHandler object containing the settings for reading the archive.\n         */\n        BIT7Z_NODISCARD const BitAbstractArchiveHandler& handler() const noexcept;\n\n        /**\n         * @brief Extracts the specified items to the chosen directory.\n         *\n         * @param out_dir   the output directory where the extracted files will be put.\n         * @param indices   the array of indices of the files in the archive that must be extracted.\n         */\n        void extract( const tstring& out_dir, const std::vector< uint32_t >& indices = {} ) const;\n\n        /**\n         * @brief Extracts a file to the output buffer.\n         *\n         * @param out_buffer   the output buffer where the content of the archive will be put.\n         * @param index        the index of the file to be extracted.\n         */\n        void extract( std::vector< byte_t >& out_buffer, uint32_t index = 0 ) const;\n\n        /**\n         * @brief Extracts a file to the pre-allocated output buffer.\n         *\n         * @tparam N     the size of the output buffer (it must be equal to the unpacked size\n         *               of the item to be extracted).\n         * @param buffer the pre-allocated output buffer.\n         * @param index  the index of the file to be extracted.\n         */\n        template< std::size_t N >\n        void extract( std::array< byte_t, N >& buffer, uint32_t index = 0 ) const {\n            extract( buffer.data(), buffer.size(), index );\n        }\n\n        /**\n         * @brief Extracts a file to the pre-allocated output buffer.\n         *\n         * @tparam N     the size of the output buffer (it must be equal to the unpacked size\n         *               of the item to be extracted).\n         * @param buffer the pre-allocated output buffer.\n         * @param index  the index of the file to be extracted.\n         */\n        template< std::size_t N >\n        void extract( byte_t (& buffer)[N], uint32_t index = 0 ) const { // NOLINT(*-avoid-c-arrays)\n            extract( buffer, N, index );\n        }\n\n        /**\n         * @brief Extracts a file to the pre-allocated output buffer.\n         *\n         * @param buffer the pre-allocated output buffer.\n         * @param size   the size of the output buffer (it must be equal to the unpacked size\n         *               of the item to be extracted).\n         * @param index  the index of the file to be extracted.\n         */\n        void extract( byte_t* buffer, std::size_t size, uint32_t index = 0 ) const;\n\n        /**\n         * @brief Extracts a file to the output stream.\n         *\n         * @param out_stream   the (binary) stream where the content of the archive will be put.\n         * @param index        the index of the file to be extracted.\n         */\n        void extract( std::ostream& out_stream, uint32_t index = 0 ) const;\n\n        /**\n         * @brief Extracts the content of the archive to a map of memory buffers, where the keys are the paths\n         * of the files (inside the archive), and the values are their decompressed contents.\n         *\n         * @param out_map   the output map.\n         */\n        void extract( std::map< tstring, std::vector< byte_t > >& out_map ) const;\n\n        /**\n         * @brief Tests the archive without extracting its content.\n         *\n         * If the archive is not valid, a BitException is thrown!\n         */\n        void test() const;\n\n    protected:\n        IInArchive* openArchiveStream( const fs::path& name, IInStream* in_stream );\n\n        HRESULT initUpdatableArchive( IOutArchive** newArc ) const;\n\n        BIT7Z_NODISCARD HRESULT close() const noexcept;\n\n        friend class BitAbstractArchiveOpener;\n\n        friend class BitAbstractArchiveCreator;\n\n        friend class BitOutputArchive;\n\n    private:\n        IInArchive* mInArchive;\n        const BitInFormat* mDetectedFormat;\n        const BitAbstractArchiveHandler& mArchiveHandler;\n        tstring mArchivePath;\n\n    public:\n        /**\n         * @brief An iterator for the elements contained in an archive.\n         */\n        class const_iterator {\n            public:\n                // iterator traits\n                using iterator_category BIT7Z_MAYBE_UNUSED = std::input_iterator_tag;\n                using value_type BIT7Z_MAYBE_UNUSED = BitArchiveItemOffset;\n                using reference = const BitArchiveItemOffset&;\n                using pointer = const BitArchiveItemOffset*;\n                using difference_type BIT7Z_MAYBE_UNUSED = uint32_t; //so that count_if returns an uint32_t\n\n                /**\n                 * @brief Advances the iterator to the next element in the archive.\n                 *\n                 * @return the iterator pointing to the next element in the archive.\n                 */\n                const_iterator& operator++() noexcept;\n\n                /**\n                 * @brief Advances the iterator to the next element in the archive.\n                 *\n                 * @return the iterator before the advancement.\n                 */\n                const_iterator operator++( int ) noexcept; // NOLINT(cert-dcl21-cpp)\n\n                /**\n                 * @brief Compares the iterator with another iterator.\n                 *\n                 * @param other Another iterator.\n                 *\n                 * @return whether the two iterators point to the same element in the archive or not.\n                 */\n                bool operator==( const const_iterator& other ) const noexcept;\n\n                /**\n                 * @brief Compares the iterator with another iterator.\n                 *\n                 * @param other Another iterator.\n                 *\n                 * @return whether the two iterators point to the different elements in the archive or not.\n                 */\n                bool operator!=( const const_iterator& other ) const noexcept;\n\n                /**\n                 * @brief Accesses the pointed-to element in the archive.\n                 *\n                 * @return a reference to the pointed-to element in the archive.\n                 */\n                reference operator*() noexcept;\n\n                /**\n                 * @brief Accesses the pointed-to element in the archive.\n                 *\n                 * @return a pointer to the pointed-to element in the archive.\n                 */\n                pointer operator->() noexcept;\n\n            private:\n                BitArchiveItemOffset mItemOffset;\n\n                const_iterator( uint32_t item_index, const BitInputArchive& item_archive ) noexcept;\n\n                friend class BitInputArchive;\n        };\n\n        /**\n         * @return an iterator to the first element of the archive. If the archive is empty,\n         *         the returned iterator will be equal to the end() iterator.\n         */\n        BIT7Z_NODISCARD BitInputArchive::const_iterator begin() const noexcept;\n\n        /**\n         * @return an iterator to the element following the last element of the archive.\n         *         This element acts as a placeholder; attempting to access it results in undefined behavior.\n         */\n        BIT7Z_NODISCARD BitInputArchive::const_iterator end() const noexcept;\n\n        /**\n         * @return an iterator to the first element of the archive. If the archive is empty,\n         *         the returned iterator will be equal to the end() iterator.\n         */\n        BIT7Z_NODISCARD BitInputArchive::const_iterator cbegin() const noexcept;\n\n        /**\n         * @return an iterator to the element following the last element of the archive.\n         *         This element acts as a placeholder; attempting to access it results in undefined behavior.\n         */\n        BIT7Z_NODISCARD BitInputArchive::const_iterator cend() const noexcept;\n\n        /**\n         * @brief Find an item in the archive that has the given path.\n         *\n         * @param path the path to be searched in the archive.\n         *\n         * @return an iterator to the item with the given path, or an iterator equal to the end() iterator\n         * if no item is found.\n         */\n        BIT7Z_NODISCARD BitInputArchive::const_iterator find( const tstring& path ) const noexcept;\n\n        /**\n         * @brief Find if there is an item in the archive that has the given path.\n         *\n         * @param path the path to be searched in the archive.\n         *\n         * @return true if and only if an item with the given path exists in the archive.\n         */\n        BIT7Z_NODISCARD bool contains( const tstring& path ) const noexcept;\n};\n\n}  // namespace bit7z\n\n#endif //BITINPUTARCHIVE_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bititemsvector.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITITEMSVECTOR_HPP\n#define BITITEMSVECTOR_HPP\n\n#include <map>\n#include <memory>\n\n#include \"bitfs.hpp\"\n#include \"bittypes.hpp\"\n\nnamespace bit7z {\n\nusing std::vector;\nusing std::map;\nusing std::unique_ptr;\n\nnamespace filesystem {\nclass FSItem;\n} // namespace filesystem\n\nusing filesystem::FSItem;\n\nstruct GenericInputItem;\nusing GenericInputItemPtr = std::unique_ptr< GenericInputItem >;\nusing GenericInputItemVector = std::vector< GenericInputItemPtr >;\n\n/** @cond **/\nstruct IndexingOptions {\n    bool recursive = true;\n    bool retain_folder_structure = false;\n    bool only_files = false;\n};\n/** @endcond **/\n\n/**\n * @brief The BitItemsVector class represents a vector of generic input items, i.e., items that can come\n * from the filesystem, from memory buffers, or from standard streams.\n */\nclass BitItemsVector final {\n    public:\n        using value_type = GenericInputItemPtr;\n\n        BitItemsVector() = default;\n\n        BitItemsVector( const BitItemsVector& ) = default;\n\n        BitItemsVector( BitItemsVector&& ) = default;\n\n        BitItemsVector& operator=( const BitItemsVector& ) = default;\n\n        BitItemsVector& operator=( BitItemsVector&& ) = default;\n\n        /**\n         * @brief Indexes the given directory, adding to the vector all the files that match the wildcard filter.\n         *\n         * @param in_dir    the directory to be indexed.\n         * @param filter    (optional) the wildcard filter to be used for indexing;\n         *                  empty string means \"index all files\".\n         * @param options   (optional) the settings to be used while indexing the given directory\n         *                  and all of its subdirectories.\n         */\n        void indexDirectory( const fs::path& in_dir, const tstring& filter = {}, IndexingOptions options = {} );\n\n        /**\n         * @brief Indexes the given vector of filesystem paths, adding to the item vector all the files.\n         *\n         * @param in_paths  the vector of filesystem paths.\n         * @param options   (optional) the settings to be used while indexing the given directory\n         *                  and all of its subdirectories.\n         */\n        void indexPaths( const std::vector< tstring >& in_paths, IndexingOptions options = {} );\n\n        /**\n         * @brief Indexes the given map of filesystem paths, adding to the vector all the files.\n         *\n         * @note Map keys represent the filesystem paths to be indexed; the corresponding mapped values are\n         * the user-defined (possibly different) paths wanted inside archives.\n         *\n         * @param in_paths  map of filesystem paths with the corresponding user-defined path desired inside the\n         *                  output archive.\n         * @param options   (optional) the settings to be used while indexing the given directory\n         *                  and all of its subdirectories.\n         */\n        void indexPathsMap( const std::map< tstring, tstring >& in_paths, IndexingOptions options = {} );\n\n        /**\n         * @brief Indexes the given file path, with an optional user-defined path to be used in output archives.\n         *\n         * @note If a directory path is given, a BitException is thrown.\n         *\n         * @param in_file the path to the filesystem file to be indexed in the vector.\n         * @param name    (optional) user-defined path to be used inside archives.\n         */\n        void indexFile( const tstring& in_file, const tstring& name = {} );\n\n        /**\n         * @brief Indexes the given buffer, using the given name as a path when compressed in archives.\n         *\n         * @param in_buffer the buffer containing the file to be indexed in the vector.\n         * @param name      user-defined path to be used inside archives.\n         */\n        void indexBuffer( const std::vector< byte_t >& in_buffer, const tstring& name );\n\n        /**\n         * @brief Indexes the given standard input stream, using the given name as a path when compressed in archives.\n         *\n         * @param in_stream the standard input stream of the file to be indexed in the vector.\n         * @param name      user-defined path to be used inside archives.\n         */\n        void indexStream( std::istream& in_stream, const tstring& name );\n\n        /**\n         * @return the size of the items vector.\n         */\n        BIT7Z_NODISCARD std::size_t size() const;\n\n        /**\n         * @param index the index of the desired item in the vector.\n         * @return a constant reference to the GenericInputItem at the given index.\n         */\n        const GenericInputItem& operator[]( GenericInputItemVector::size_type index ) const;\n\n        /**\n         * @return an iterator to the first element of the vector. If the vector is empty,\n         *         the returned iterator will be equal to the end() iterator.\n         */\n        BIT7Z_NODISCARD GenericInputItemVector::const_iterator begin() const noexcept;\n\n        /**\n         * @return an iterator to the element following the last element of the vector.\n         *         This element acts as a placeholder; attempting to access it results in undefined behavior.\n         */\n        BIT7Z_NODISCARD GenericInputItemVector::const_iterator end() const noexcept;\n\n        /**\n         * @return an iterator to the first element of the vector. If the vector is empty,\n         *         the returned iterator will be equal to the end() iterator.\n         */\n        BIT7Z_NODISCARD GenericInputItemVector::const_iterator cbegin() const noexcept;\n\n        /**\n         * @return an iterator to the element following the last element of the vector.\n         *         This element acts as a placeholder; attempting to access it results in undefined behavior.\n         */\n        BIT7Z_NODISCARD GenericInputItemVector::const_iterator cend() const noexcept;\n\n        ~BitItemsVector();\n\n    private:\n        GenericInputItemVector mItems;\n\n        void indexItem( const FSItem& item, IndexingOptions options );\n};\n\n}  // namespace bit7z\n\n#endif //BITITEMSVECTOR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitmemcompressor.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITMEMCOMPRESSOR_HPP\n#define BITMEMCOMPRESSOR_HPP\n\n#include \"bitcompressor.hpp\"\n\nnamespace bit7z {\n\n/**\n * @brief The BitMemCompressor alias allows compressing memory buffers.\n * The compressed archives can be saved to the filesystem, standard streams, or memory buffers.\n *\n * It let decide various properties of the produced archive, such as the password\n * protection and the compression level desired.\n */\nusing BitMemCompressor BIT7Z_MAYBE_UNUSED = BitCompressor< const std::vector< byte_t >& >;\n\n} // namespace bit7z\n#endif // BITMEMCOMPRESSOR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitmemextractor.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITMEMEXTRACTOR_HPP\n#define BITMEMEXTRACTOR_HPP\n\n#include \"bitextractor.hpp\"\n\nnamespace bit7z {\n\n/**\n * @brief The BitMemExtractor alias allows extracting the content of in-memory archives.\n */\nusing BitMemExtractor BIT7Z_MAYBE_UNUSED = BitExtractor< const std::vector< byte_t >& >;\n\n} // namespace bit7z\n\n#endif // BITMEMEXTRACTOR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitoutputarchive.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITOUTPUTARCHIVE_HPP\n#define BITOUTPUTARCHIVE_HPP\n\n#include <istream>\n#include <set>\n\n#include \"bitabstractarchivecreator.hpp\"\n#include \"bititemsvector.hpp\"\n#include \"bitexception.hpp\" //for FailedFiles\n#include \"bitpropvariant.hpp\"\n\nstruct ISequentialInStream;\n\nnamespace bit7z {\n\nusing std::istream;\n\nusing DeletedItems = std::set< uint32_t >;\n\n/* General note: I tried my best to explain how indices work here, but it is a bit complex. */\n\n/* We introduce a strong index type to differentiate between indices in the output\n * archive (uint32_t, as used by the UpdateCallback), and the corresponding indexes\n * in the input archive (input_index). In this way, we avoid implicit conversions\n * between the two kinds of indices.\n *\n * UpdateCallback uses indices in the range [0, BitOutputArchive::itemsCount() - 1]\n *\n * Now, if the user doesn't delete any item in the input archive, itemsCount()\n * is just equal to <n. of items in the input archive> + <n. of newly added items>.\n * In this case, an input_index value is just equal to the index used by UpdateCallback.\n *\n * On the contrary, if the user wants to delete an item in the input archive, the value\n * of an input_index may differ from the corresponding UpdateCallback's index.\n *\n * Note: given an input_index i:\n *         if i < mInputArchiveItemsCount, the item is old (old item in the input archive);\n *         if i >= mInputArchiveItemsCount, the item is new (added by the user); */\nenum class input_index : std::uint32_t {};\n\nclass UpdateCallback;\n\n/**\n * @brief The BitOutputArchive class, given a creator object, allows creating new archives.\n */\nclass BitOutputArchive {\n    public:\n        /**\n         * @brief Constructs a BitOutputArchive object for a completely new archive.\n         *\n          * @param creator  the reference to the BitAbstractArchiveCreator object containing all the settings to\n         *                  be used for creating the new archive.\n         */\n        explicit BitOutputArchive( const BitAbstractArchiveCreator& creator );\n\n        /**\n         * @brief Constructs a BitOutputArchive object, opening an (optional) input file archive.\n         *\n         * If a non-empty input file path is passed, the corresponding archive will be opened and\n         * used as a base for the creation of the new archive. Otherwise, the class will behave\n         * as if it is creating a completely new archive.\n         *\n         * @param creator the reference to the BitAbstractArchiveCreator object containing all the settings to\n         *                be used for creating the new archive and reading the (optional) input archive.\n         * @param in_file (optional) the path to an input archive file.\n         */\n        explicit BitOutputArchive( const BitAbstractArchiveCreator& creator, const tstring& in_file );\n\n        /**\n         * @brief Constructs a BitOutputArchive object, opening an input file archive from the given buffer.\n         *\n         * If a non-empty input buffer is passed, the archive file it contains will be opened and\n         * used as a base for the creation of the new archive. Otherwise, the class will behave\n         * as if it is creating a completely new archive.\n         *\n         * @param creator   the reference to the BitAbstractArchiveCreator object containing all the settings to\n         *                  be used for creating the new archive and reading the (optional) input archive.\n         * @param in_buffer the buffer containing an input archive file.\n         */\n        BitOutputArchive( const BitAbstractArchiveCreator& creator, const std::vector< byte_t >& in_buffer );\n\n        /**\n         * @brief Constructs a BitOutputArchive object, reading an input file archive from the given std::istream.\n         *\n         * @param creator   the reference to the BitAbstractArchiveCreator object containing all the settings to\n         *                  be used for creating the new archive and reading the (optional) input archive.\n         * @param in_stream the standard input stream of the input archive file.\n         */\n        BitOutputArchive( const BitAbstractArchiveCreator& creator, std::istream& in_stream );\n\n        BitOutputArchive( const BitOutputArchive& ) = delete;\n\n        BitOutputArchive( BitOutputArchive&& ) = delete;\n\n        BitOutputArchive& operator=( const BitOutputArchive& ) = delete;\n\n        BitOutputArchive& operator=( BitOutputArchive&& ) = delete;\n\n        /**\n         * @brief Adds all the items that can be found by indexing the given vector of filesystem paths.\n         *\n         * @param in_paths the vector of filesystem paths.\n         */\n        void addItems( const std::vector< tstring >& in_paths );\n\n        /**\n         * @brief Adds all the items that can be found by indexing the keys of the given map of filesystem paths;\n         *       the corresponding mapped values are the user-defined paths wanted inside the output archive.\n         *\n         * @param in_paths map of filesystem paths with the corresponding user-defined path desired inside the\n         *                 output archive.\n         */\n        void addItems( const std::map< tstring, tstring >& in_paths );\n\n        /**\n         * @brief Adds the given file path, with an optional user-defined path to be used in the output archive.\n         *\n         * @note If a directory path is given, a BitException is thrown.\n         *\n         * @param in_file the path to the filesystem file to be added to the output archive.\n         * @param name    (optional) user-defined path to be used inside the output archive.\n         */\n        void addFile( const tstring& in_file, const tstring& name = {} );\n\n        /**\n         * @brief Adds the given buffer file, using the given name as a path when compressed in the output archive.\n         *\n         * @param in_buffer the buffer containing the file to be added to the output archive.\n         * @param name      user-defined path to be used inside the output archive.\n         */\n        void addFile( const std::vector< byte_t >& in_buffer, const tstring& name );\n\n        /**\n         * @brief Adds the given standard input stream, using the given name as a path when compressed\n         *        in the output archive.\n         *\n         * @param in_stream\n         * @param name\n         */\n        void addFile( std::istream& in_stream, const tstring& name );\n\n        /**\n         * @brief Adds all the files in the given vector of filesystem paths.\n         *\n         * @note Paths to directories are ignored.\n         *\n         * @param in_files the vector of paths to files.\n         */\n        void addFiles( const std::vector< tstring >& in_files );\n\n        /**\n         * @brief Adds all the files inside the given directory path that match the given wildcard filter.\n         *\n         * @param in_dir    the directory where to search for files to be added to the output archive.\n         * @param filter    (optional) the wildcard filter to be used for searching the files.\n         * @param recursive (optional) recursively search the files in the given directory\n         *                  and all of its subdirectories.\n         */\n        void addFiles( const tstring& in_dir,\n                       const tstring& filter = BIT7Z_STRING( \"*.*\" ),\n                       bool recursive = true );\n\n        /**\n         * @brief Adds all the items inside the given directory path.\n         *\n         * @param in_dir the directory where to search for items to be added to the output archive.\n         */\n        void addDirectory( const tstring& in_dir );\n\n        /**\n         * @brief Compresses all the items added to this object to the specified archive file path.\n         *\n         * @note If this object was created by passing an input archive file path, and this latter is the same as\n         * the out_file path parameter, the file will be updated.\n         *\n         * @param out_file the output archive file path.\n         */\n        void compressTo( const tstring& out_file );\n\n        /**\n         * @brief Compresses all the items added to this object to the specified buffer.\n         *\n         * @param out_buffer the output buffer.\n         */\n        void compressTo( std::vector< byte_t >& out_buffer );\n\n        /**\n         * @brief Compresses all the items added to this object to the specified buffer.\n         *\n         * @param out_stream the output standard stream.\n         */\n        void compressTo( std::ostream& out_stream );\n\n        /**\n         * @return the total number of items added to the output archive object.\n         */\n        uint32_t itemsCount() const;\n\n        /**\n         * @return a constant reference to the BitAbstractArchiveHandler object containing the\n         *         settings for writing the output archive.\n         */\n        const BitAbstractArchiveHandler& handler() const noexcept;\n\n        /**\n         * @brief Default destructor.\n         */\n        virtual ~BitOutputArchive() = default;\n\n    protected:\n        virtual BitPropVariant itemProperty( input_index index, BitProperty prop ) const;\n\n        virtual HRESULT itemStream( input_index index, ISequentialInStream** inStream ) const;\n\n        virtual bool hasNewData( uint32_t index ) const noexcept;\n\n        virtual bool hasNewProperties( uint32_t index ) const noexcept;\n\n        input_index itemInputIndex( uint32_t new_index ) const noexcept;\n\n        BitPropVariant outputItemProperty( uint32_t index, BitProperty propID ) const;\n\n        HRESULT outputItemStream( uint32_t index, ISequentialInStream** inStream ) const;\n\n        uint32_t indexInArchive( uint32_t index ) const noexcept;\n\n        inline BitInputArchive* inputArchive() const {\n            return mInputArchive.get();\n        }\n\n        inline void setInputArchive( std::unique_ptr< BitInputArchive >&& input_archive ) {\n            mInputArchive = std::move( input_archive );\n        }\n\n        inline uint32_t inputArchiveItemsCount() const {\n            return mInputArchiveItemsCount;\n        }\n\n        inline void setDeletedIndex( uint32_t index ) {\n            mDeletedItems.insert( index );\n        }\n\n        inline bool isDeletedIndex( uint32_t index ) const {\n            return mDeletedItems.find( index ) != mDeletedItems.cend();\n        }\n\n        inline bool hasDeletedIndexes() const {\n            return !mDeletedItems.empty();\n        }\n\n        inline bool hasNewItems() const {\n            return mNewItemsVector.size() > 0;\n        }\n\n        friend class UpdateCallback;\n\n    private:\n        const BitAbstractArchiveCreator& mArchiveCreator;\n\n        unique_ptr< BitInputArchive > mInputArchive;\n        uint32_t mInputArchiveItemsCount;\n\n        BitItemsVector mNewItemsVector;\n        DeletedItems mDeletedItems;\n\n        mutable FailedFiles mFailedFiles;\n\n        /* mInputIndices:\n         *   Position i = index in range [0, itemsCount() - 1] used by UpdateCallback.\n         *   Value at pos. i = corresponding index in the input archive (type input_index).\n         *\n         * If there are some deleted items, then i != mInputIndices[i]\n         * (at least for values of i greater than the index of the first deleted item).\n         *\n         * Otherwise, if there are no deleted items, mInputIndices is empty, and itemInputIndex(i)\n         * will return input_index with value i.\n         *\n         * This vector is either empty, or it has size equal to itemsCount() (thanks to updateInputIndices()). */\n        std::vector< input_index > mInputIndices;\n\n        CMyComPtr< IOutArchive > initOutArchive() const;\n\n        CMyComPtr< IOutStream > initOutFileStream( const fs::path& out_archive, bool updating_archive ) const;\n\n#if defined( _WIN32 ) && defined( BIT7Z_AUTO_PREFIX_LONG_PATHS )\n        BitOutputArchive( const BitAbstractArchiveCreator& creator, fs::path in_arc );\n#else\n        BitOutputArchive( const BitAbstractArchiveCreator& creator, const fs::path& in_arc );\n#endif\n\n        void compressToFile( const fs::path& out_file, UpdateCallback* update_callback );\n\n        void compressOut( IOutArchive* out_arc,\n                          IOutStream* out_stream,\n                          UpdateCallback* update_callback );\n\n        void setArchiveProperties( IOutArchive* out_archive ) const;\n\n        void updateInputIndices();\n};\n\n}  // namespace bit7z\n\n#endif //BITOUTPUTARCHIVE_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitpropvariant.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITPROPVARIANT_HPP\n#define BITPROPVARIANT_HPP\n\n#include <chrono>\n#include <cstdint>\n\n#include \"bitdefines.hpp\"\n#include \"bittypes.hpp\"\n#include \"bitwindows.hpp\"\n\nnamespace bit7z {\n\n/**\n * @brief A type representing a time point measured using the system clock.\n */\nusing time_type = std::chrono::time_point< std::chrono::system_clock >;\n\n/**\n * @brief The BitProperty enum represents the archive/item properties that 7-zip can read or write.\n */\nenum struct BitProperty : PROPID {\n    NoProperty = 0,         ///<\n    MainSubfile,            ///<\n    HandlerItemIndex,       ///<\n    Path,                   ///<\n    Name,                   ///<\n    Extension,              ///<\n    IsDir,                  ///<\n    Size,                   ///<\n    PackSize,               ///<\n    Attrib,                 ///<\n    CTime,                  ///<\n    ATime,                  ///<\n    MTime,                  ///<\n    Solid,                  ///<\n    Commented,              ///<\n    Encrypted,              ///<\n    SplitBefore,            ///<\n    SplitAfter,             ///<\n    DictionarySize,         ///<\n    CRC,                    ///<\n    Type,                   ///<\n    IsAnti,                 ///<\n    Method,                 ///<\n    HostOS,                 ///<\n    FileSystem,             ///<\n    User,                   ///<\n    Group,                  ///<\n    Block,                  ///<\n    Comment,                ///<\n    Position,               ///<\n    Prefix,                 ///<\n    NumSubDirs,             ///<\n    NumSubFiles,            ///<\n    UnpackVer,              ///<\n    Volume,                 ///<\n    IsVolume,               ///<\n    Offset,                 ///<\n    Links,                  ///<\n    NumBlocks,              ///<\n    NumVolumes,             ///<\n    TimeType,               ///<\n    Bit64,                  ///<\n    BigEndian,              ///<\n    Cpu,                    ///<\n    PhySize,                ///<\n    HeadersSize,            ///<\n    Checksum,               ///<\n    Characts,               ///<\n    Va,                     ///<\n    Id,                     ///<\n    ShortName,              ///<\n    CreatorApp,             ///<\n    SectorSize,             ///<\n    PosixAttrib,            ///<\n    SymLink,                ///<\n    Error,                  ///<\n    TotalSize,              ///<\n    FreeSpace,              ///<\n    ClusterSize,            ///<\n    VolumeName,             ///<\n    LocalName,              ///<\n    Provider,               ///<\n    NtSecure,               ///<\n    IsAltStream,            ///<\n    IsAux,                  ///<\n    IsDeleted,              ///<\n    IsTree,                 ///<\n    Sha1,                   ///<\n    Sha256,                 ///<\n    ErrorType,              ///<\n    NumErrors,              ///<\n    ErrorFlags,             ///<\n    WarningFlags,           ///<\n    Warning,                ///<\n    NumStreams,             ///<\n    NumAltStreams,          ///<\n    AltStreamsSize,         ///<\n    VirtualSize,            ///<\n    UnpackSize,             ///<\n    TotalPhySize,           ///<\n    VolumeIndex,            ///<\n    SubType,                ///<\n    ShortComment,           ///<\n    CodePage,               ///<\n    IsNotArcType,           ///<\n    PhySizeCantBeDetected,  ///<\n    ZerosTailIsAllowed,     ///<\n    TailSize,               ///<\n    EmbeddedStubSize,       ///<\n    NtReparse,              ///<\n    HardLink,               ///<\n    INode,                  ///<\n    StreamId,               ///<\n    ReadOnly,               ///<\n    OutName,                ///<\n    CopyLink                ///<\n};\n\n/**\n * @brief The BitPropVariantType enum represents the possible types that a BitPropVariant can store.\n */\nenum struct BitPropVariantType : uint32_t {\n    Empty,      ///< Empty BitPropVariant type\n    Bool,       ///< Boolean BitPropVariant type\n    String,     ///< String BitPropVariant type\n    UInt8,      ///< 8-bit unsigned int BitPropVariant type\n    UInt16,     ///< 16-bit unsigned int BitPropVariant type\n    UInt32,     ///< 32-bit unsigned int BitPropVariant type\n    UInt64,     ///< 64-bit unsigned int BitPropVariant type\n    Int8,       ///< 8-bit signed int BitPropVariant type\n    Int16,      ///< 16-bit signed int BitPropVariant type\n    Int32,      ///< 32-bit signed int BitPropVariant type\n    Int64,      ///< 64-bit signed int BitPropVariant type\n    FileTime    ///< FILETIME BitPropVariant type\n};\n\n/**\n * @brief The BitPropVariant struct is a light extension to the WinAPI PROPVARIANT struct providing useful getters.\n */\nstruct BitPropVariant final : public PROPVARIANT {\n        /**\n         * @brief Constructs an empty BitPropVariant object.\n         */\n        BitPropVariant();\n\n        /**\n         * @brief Copy constructs this BitPropVariant from another one.\n         *\n         * @param other the variant to be copied.\n         */\n        BitPropVariant( const BitPropVariant& other );\n\n        /**\n         * @brief Move constructs this BitPropVariant from another one.\n         *\n         * @param other the variant to be moved.\n         */\n        BitPropVariant( BitPropVariant&& other ) noexcept;\n\n        /**\n         * @brief Constructs a boolean BitPropVariant\n         *\n         * @param value the bool value of the BitPropVariant\n         */\n        explicit BitPropVariant( bool value ) noexcept;\n\n        /**\n         * @brief Constructs a string BitPropVariant from a null-terminated C wide string\n         *\n         * @param value the null-terminated C wide string value of the BitPropVariant\n         */\n        explicit BitPropVariant( const wchar_t* value );\n\n        /**\n         * @brief Constructs a string BitPropVariant from a wstring\n         *\n         * @param value the wstring value of the BitPropVariant\n         */\n        explicit BitPropVariant( const std::wstring& value );\n\n        /**\n         * @brief Constructs an 8-bit unsigned integer BitPropVariant\n         *\n         * @param value the uint8_t value of the BitPropVariant\n         */\n        explicit BitPropVariant( uint8_t value ) noexcept;\n\n        /**\n         * @brief Constructs a 16-bit unsigned integer BitPropVariant\n         *\n         * @param value the uint16_t value of the BitPropVariant\n         */\n        explicit BitPropVariant( uint16_t value ) noexcept;\n\n        /**\n         * @brief Constructs a 32-bit unsigned integer BitPropVariant\n         *\n         * @param value the uint32_t value of the BitPropVariant\n         */\n        explicit BitPropVariant( uint32_t value ) noexcept;\n\n        /**\n         * @brief Constructs a 64-bit unsigned integer BitPropVariant\n         *\n         * @param value the uint64_t value of the BitPropVariant\n         */\n        explicit BitPropVariant( uint64_t value ) noexcept;\n\n        /**\n         * @brief Constructs an 8-bit integer BitPropVariant\n         *\n         * @param value the int8_t value of the BitPropVariant\n         */\n        explicit BitPropVariant( int8_t value ) noexcept;\n\n        /**\n         * @brief Constructs a 16-bit integer BitPropVariant\n         *\n         * @param value the int16_t value of the BitPropVariant\n         */\n        explicit BitPropVariant( int16_t value ) noexcept;\n\n        /**\n         * @brief Constructs a 32-bit integer BitPropVariant\n         *\n         * @param value the int32_t value of the BitPropVariant\n         */\n        explicit BitPropVariant( int32_t value ) noexcept;\n\n        /**\n         * @brief Constructs a 64-bit integer BitPropVariant\n         *\n         * @param value the int64_t value of the BitPropVariant\n         */\n        explicit BitPropVariant( int64_t value ) noexcept;\n\n        /**\n         * @brief Constructs a FILETIME BitPropVariant\n         *\n         * @param value the FILETIME value of the BitPropVariant\n         */\n        explicit BitPropVariant( const FILETIME& value ) noexcept;\n\n        /**\n         * @brief BitPropVariant destructor.\n         *\n         * @note This is not virtual to maintain the same memory layout of the base struct!\n         */\n        ~BitPropVariant();\n\n        /**\n         * @brief Copy assignment operator.\n         *\n         * @param other the variant to be copied.\n         *\n         * @return a reference to *this object (with the copied values from other).\n         */\n        BitPropVariant& operator=( const BitPropVariant& other );\n\n        /**\n         * @brief Move assignment operator.\n         *\n         * @param other the variant to be moved.\n         *\n         * @return a reference to *this object (with the moved values from other).\n         */\n        BitPropVariant& operator=( BitPropVariant&& other ) noexcept;\n\n        /**\n         * @brief Assignment operator\n         *\n         * @note this will work only for T types for which a BitPropVariant constructor is defined!\n         *\n         * @param value the value to be assigned to the object\n         *\n         * @return a reference to *this object having the value as new variant value\n         */\n        template< typename T >\n        BitPropVariant& operator=( const T& value ) noexcept( std::is_integral< T >::value ) {\n            *this = BitPropVariant( value );\n            return *this;\n        }\n\n        /**\n         * @return the boolean value of this variant\n         * (it throws an exception if the variant is not a boolean value).\n         */\n        BIT7Z_NODISCARD bool getBool() const;\n\n        /**\n         * @return the string value of this variant\n         * (it throws an exception if the variant is not a string).\n         */\n        BIT7Z_NODISCARD tstring getString() const;\n\n        /**\n         * @return the 8-bit unsigned integer value of this variant\n         * (it throws an exception if the variant is not an 8-bit unsigned integer).\n         */\n        BIT7Z_NODISCARD uint8_t getUInt8() const;\n\n        /**\n         * @return the 16-bit unsigned integer value of this variant\n         * (it throws an exception if the variant is not an 8 or 16-bit unsigned integer).\n         */\n        BIT7Z_NODISCARD uint16_t getUInt16() const;\n\n        /**\n         * @return the 32-bit unsigned integer value of this variant\n         * (it throws an exception if the variant is not an 8, 16 or 32-bit unsigned integer).\n         */\n        BIT7Z_NODISCARD uint32_t getUInt32() const;\n\n        /**\n         * @return the 64-bit unsigned integer value of this variant\n         * (it throws an exception if the variant is not an 8, 16, 32 or 64-bit unsigned integer).\n         */\n        BIT7Z_NODISCARD uint64_t getUInt64() const;\n\n        /**\n         * @return the 8-bit integer value of this variant\n         * (it throws an exception if the variant is not an 8-bit integer).\n         */\n        BIT7Z_NODISCARD int8_t getInt8() const;\n\n        /**\n         * @return the 16-bit integer value of this variant\n         * (it throws an exception if the variant is not an 8 or 16-bit integer).\n         */\n        BIT7Z_NODISCARD int16_t getInt16() const;\n\n        /**\n         * @return the 32-bit integer value of this variant\n         * (it throws an exception if the variant is not an 8, 16 or 32-bit integer).\n         */\n        BIT7Z_NODISCARD int32_t getInt32() const;\n\n        /**\n         * @return the 64-bit integer value of this variant\n         * (it throws an exception if the variant is not an 8, 16, 32 or 64-bit integer).\n         */\n        BIT7Z_NODISCARD int64_t getInt64() const;\n\n        /**\n         * @return the FILETIME value of this variant\n         * (it throws an exception if the variant is not a filetime).\n         */\n        BIT7Z_NODISCARD FILETIME getFileTime() const;\n\n        /**\n         * @return the FILETIME value of this variant converted to std::time_point\n         * (it throws an exception if the variant is not a filetime).\n         */\n        BIT7Z_NODISCARD time_type getTimePoint() const;\n\n        /**\n         * @return the value of this variant converted from any supported type to std::wstring.\n         */\n        BIT7Z_NODISCARD tstring toString() const;\n\n        /**\n         * @return a boolean value indicating whether the variant is empty.\n         */\n        BIT7Z_NODISCARD bool isEmpty() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether the variant is a boolean value.\n         */\n        BIT7Z_NODISCARD bool isBool() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether the variant is a string.\n         */\n        BIT7Z_NODISCARD bool isString() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether the variant is an 8-bit unsigned integer.\n         */\n        BIT7Z_NODISCARD bool isUInt8() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether the variant is an 8 or 16-bit unsigned integer.\n         */\n        BIT7Z_NODISCARD bool isUInt16() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether the variant is an 8, 16 or 32-bit unsigned integer.\n         */\n        BIT7Z_NODISCARD bool isUInt32() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether the variant is an 8, 16, 32 or 64-bit unsigned integer.\n         */\n        BIT7Z_NODISCARD bool isUInt64() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether the variant is an 8-bit integer.\n         */\n        BIT7Z_NODISCARD bool isInt8() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether the variant is an 8 or 16-bit integer.\n         */\n        BIT7Z_NODISCARD bool isInt16() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether the variant is an 8, 16 or 32-bit integer.\n         */\n        BIT7Z_NODISCARD bool isInt32() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether the variant is an 8, 16, 32 or 64-bit integer.\n         */\n        BIT7Z_NODISCARD bool isInt64() const noexcept;\n\n        /**\n         * @return a boolean value indicating whether the variant is a FILETIME structure.\n         */\n        BIT7Z_NODISCARD bool isFileTime() const noexcept;\n\n        /**\n         * @return the BitPropVariantType of this variant.\n         */\n        BIT7Z_NODISCARD BitPropVariantType type() const;\n\n        /**\n         * @brief Clears the current value of the variant object\n         */\n        void clear() noexcept;\n\n    private:\n        void internalClear() noexcept;\n\n        friend bool operator==( const BitPropVariant& lhs, const BitPropVariant& rhs ) noexcept;\n\n        friend bool operator!=( const BitPropVariant& lhs, const BitPropVariant& rhs ) noexcept;\n};\n\nbool operator==( const BitPropVariant& lhs, const BitPropVariant& rhs ) noexcept;\n\nbool operator!=( const BitPropVariant& lhs, const BitPropVariant& rhs ) noexcept;\n\n}  // namespace bit7z\n\n#endif // BITPROPVARIANT_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitstreamcompressor.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITSTREAMCOMPRESSOR_HPP\n#define BITSTREAMCOMPRESSOR_HPP\n\n#include \"bitcompressor.hpp\"\n\nnamespace bit7z {\n\n/**\n * @brief The BitStreamCompressor alias allows compressing data from standard input streams.\n * The compressed archives can be saved to the filesystem, standard streams, or memory buffers.\n *\n * It let decide various properties of the produced archive, such as the password\n * protection and the compression level desired.\n */\nusing BitStreamCompressor BIT7Z_MAYBE_UNUSED = BitCompressor< std::istream& >;\n\n} // namespace bit7z\n\n#endif // BITSTREAMCOMPRESSOR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitstreamextractor.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITSTREAMEXTRACTOR_HPP\n#define BITSTREAMEXTRACTOR_HPP\n\n#include \"bitextractor.hpp\"\n\nnamespace bit7z {\n\n/**\n * @brief The BitStreamExtractor alias allows extracting the content of in-memory archives.\n */\nusing BitStreamExtractor BIT7Z_MAYBE_UNUSED = BitExtractor< std::istream& >;\n\n} // namespace bit7z\n\n#endif // BITSTREAMEXTRACTOR_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bittypes.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITTYPES_HPP\n#define BITTYPES_HPP\n\n#include <string>\n#include <vector>\n\n#ifdef BIT7Z_REGEX_MATCHING\n#include <regex>\n#endif\n\nnamespace bit7z {\n\n/**\n * @brief A type representing a byte.\n */\n#ifdef BIT7Z_USE_STD_BYTE\n#if __cpp_lib_byte\nusing byte_t = std::byte;\n#else\nenum class byte_t : unsigned char {}; //same as std::byte_t\n#endif\n#else\nusing byte_t = unsigned char;\n#endif\n\n/** @cond */\nusing buffer_t = std::vector< byte_t >;\nusing index_t = std::ptrdiff_t; //like gsl::index (https://github.com/microsoft/GSL)\n\ntemplate< class Char >\nstruct string_traits;\n\ntemplate<>\nstruct string_traits< char > {\n    template< class T >\n    static inline std::string convert_to_string( T&& t ) {\n        return std::to_string( std::forward< T >( t ) );\n    }\n};\n\ntemplate<>\nstruct string_traits< wchar_t > {\n    template< class T >\n    static inline std::wstring convert_to_string( T&& t ) {\n        return std::to_wstring( std::forward< T >( t ) );\n    }\n};\n/** @endcond */\n\n/**\n * @note On Windows, if the `BIT7Z_USE_NATIVE_STRING` option is enabled, `tchar` is an alias of `wchar_t`.\n */\n#if defined( BIT7Z_USE_NATIVE_STRING ) && defined( _WIN32 ) // Windows with native strings\nusing tchar = wchar_t;\n#define BIT7Z_STRING( str ) L##str\n#else // Unix, and Windows with non-native strings\nusing tchar = char;\n#define BIT7Z_STRING( str ) str\n#endif\n\n/**\n * @note On Windows, if the `BIT7Z_USE_NATIVE_STRING` option is enabled, `tstring` is equivalent to a std::wstring.\n * Otherwise, it is equivalent to a std::string (default alias).\n */\nusing tstring = std::basic_string< tchar >;\n\n#ifdef BIT7Z_REGEX_MATCHING\n/**\n * @note On Windows, if the `BIT7Z_USE_NATIVE_STRING` option is enabled, `tregex` is equivalent to a std::wregex.\n * Otherwise, it is equivalent to a std::regex (default alias).\n */\nusing tregex = std::basic_regex< tchar >;\n#endif\n\ntemplate< typename T >\ninline std::basic_string< tchar > to_tstring( T&& arg ) {\n    return string_traits< tchar >::convert_to_string( std::forward< T >( arg ) );\n}\n\n}  // namespace bit7z\n\n#endif // BITTYPES_HPP\n"
  },
  {
    "path": "third-party/bit7z/include/include/bit7z/bitwindows.hpp",
    "content": "/*\n * bit7z - A C++ static library to interface with the 7-zip shared libraries.\n * Copyright (c) 2014-2022 Riccardo Ostani - All Rights Reserved.\n *\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n#ifndef BITWINDOWS_HPP\n#define BITWINDOWS_HPP\n\n#ifdef _WIN32\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n\n#ifndef NOMINMAX\n#define NOMINMAX\n#endif\n#include <Windows.h>\n#include <propidl.h>\n#else\n/* We don't have the \"Windows.h\" header on Unix systems, so in theory, we could use the \"MyWindows.h\" of p7zip/7-zip.\n * However, some of bit7z's public API headers need some Win32 API structs like PROPVARIANT and GUID.\n * Hence, it would result in the leak of p7zip/7-zip headers, making bit7z's clients dependent on them.\n * Also, (publicly) forward declaring them and then (internally) using the \"MyWindows.h\" is impossible:\n * the two different declarations would conflict, making the compilation fail.\n *\n * To avoid all these issues, we define the required Win32 API structs, constants, and type aliases,\n * with the same definitions in the MyWindows.h header.\n * We will use only this header and avoid including \"MyWindows.h\" or similar headers (e.g., StdAfx.h). */\n#include <cerrno>\n#include <cstdint>\n\n// Avoiding accidentally including p7zip's MyWindows.h, so that its inclusion is not needed in client code!\n#ifndef __MYWINDOWS_H\n#define __MYWINDOWS_H\n#endif\n\n// Avoiding accidentally including 7-zip's MyWindows.h, so that its inclusion is not needed in client code!\n#ifndef __MY_WINDOWS_H\n#define __MY_WINDOWS_H\n#endif\n\nusing std::size_t;\n\n#define WINAPI\n\nnamespace bit7z {\n\n// Win32 type aliases\nusing HMODULE = void*;\nusing HRESULT = int;\nusing OLECHAR = wchar_t;\nusing BSTR = OLECHAR*;\nusing VARIANT_BOOL = short;\nusing VARTYPE = unsigned short;\n\nusing WORD = unsigned short;\nusing DWORD = unsigned int;\n\nusing ULONG = unsigned int;\nusing PROPID = ULONG;\n\n// Error codes constants can be useful for bit7z's clients on Unix (since they don't have the Windows.h header).\n\n#ifndef S_OK // Silencing cppcheck warning on E_NOTIMPL, probably a bug of cppcheck.\n// Win32 HRESULT error codes.\nconstexpr auto S_OK = static_cast< HRESULT >( 0x00000000L );\nconstexpr auto S_FALSE = static_cast< HRESULT >( 0x00000001L );\nconstexpr auto E_NOTIMPL = static_cast< HRESULT >( 0x80004001L );\nconstexpr auto E_NOINTERFACE = static_cast< HRESULT >( 0x80004002L );\nconstexpr auto E_ABORT = static_cast< HRESULT >( 0x80004004L );\nconstexpr auto E_FAIL = static_cast< HRESULT >( 0x80004005L );\nconstexpr auto STG_E_INVALIDFUNCTION = static_cast< HRESULT >( 0x80030001L );\nconstexpr auto E_OUTOFMEMORY = static_cast< HRESULT >( 0x8007000EL );\nconstexpr auto E_INVALIDARG = static_cast< HRESULT >( 0x80070057L );\n#endif\n\n#ifndef ERROR_ALREADY_EXISTS\n// Win32 error codes (defined by both p7zip and 7-zip as equivalent to POSIX error codes).\nconstexpr auto ERROR_ALREADY_EXISTS = EEXIST;\nconstexpr auto ERROR_DISK_FULL = ENOSPC;\nconstexpr auto ERROR_FILE_EXISTS = EEXIST;\nconstexpr auto ERROR_FILE_NOT_FOUND = ENOENT;\nconstexpr auto ERROR_INVALID_PARAMETER = EINVAL;\nconstexpr auto ERROR_INVALID_FUNCTION = EINVAL;\nconstexpr auto ERROR_INVALID_HANDLE = EBADF;\nconstexpr auto ERROR_OPEN_FAILED = EIO;\nconstexpr auto ERROR_PATH_NOT_FOUND = ENOENT;\nconstexpr auto ERROR_SEEK = EIO;\nconstexpr auto ERROR_READ_FAULT = EIO;\nconstexpr auto ERROR_WRITE_FAULT = EIO;\n\n// Win32 error codes (defined by p7zip with the same values as in Windows API).\nconstexpr auto ERROR_NO_MORE_FILES = 0x100018;\nconstexpr auto ERROR_DIRECTORY = 267;\n#endif\n\n// Win32 structs.\nstruct FILETIME {\n    DWORD dwLowDateTime;\n    DWORD dwHighDateTime;\n};\n\nstruct LARGE_INTEGER {\n    int64_t QuadPart;\n};\n\nstruct ULARGE_INTEGER {\n    uint64_t QuadPart;\n};\n\nstruct PROPVARIANT {\n    VARTYPE vt;\n    WORD wReserved1;\n    WORD wReserved2;\n    WORD wReserved3;\n    union {\n        char cVal;\n        unsigned char bVal;\n        short iVal;\n        unsigned short uiVal;\n        int lVal;\n        unsigned int ulVal;\n        int intVal;\n        unsigned int uintVal;\n        LARGE_INTEGER hVal;\n        ULARGE_INTEGER uhVal;\n        VARIANT_BOOL boolVal;\n        int scode;\n        FILETIME filetime;\n        BSTR bstrVal;\n    };\n};\n\n}  // namespace bit7z\n\n#endif\n\n#endif //BITWINDOWS_HPP\n"
  },
  {
    "path": "third-party/imgui/LICENSE.txt",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014-2023 Omar Cornut\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": "third-party/imgui/imconfig.h",
    "content": "//-----------------------------------------------------------------------------\n// DEAR IMGUI COMPILE-TIME OPTIONS\n// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.\n// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.\n//-----------------------------------------------------------------------------\n// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)\n// B) or '#define IMGUI_USER_CONFIG \"my_imgui_config.h\"' in your project and then add directives in your own file without touching this template.\n//-----------------------------------------------------------------------------\n// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp\n// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.\n// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.\n// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.\n//-----------------------------------------------------------------------------\n\n#pragma once\n\n//---- Define assertion handler. Defaults to calling assert().\n// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.\n//#define IM_ASSERT(_EXPR)  MyAssert(_EXPR)\n//#define IM_ASSERT(_EXPR)  ((void)(_EXPR))     // Disable asserts\n\n//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows\n// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.\n// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()\n// for each static/DLL boundary you are calling from. Read \"Context and Memory Allocators\" section of imgui.cpp for more details.\n//#define IMGUI_API __declspec( dllexport )\n//#define IMGUI_API __declspec( dllimport )\n\n//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.\n//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n//#define IMGUI_DISABLE_OBSOLETE_KEYIO                      // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.\n\n//---- Disable all of Dear ImGui or don't implement standard windows/tools.\n// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.\n//#define IMGUI_DISABLE                                     // Disable everything: all headers and source files will be empty.\n//#define IMGUI_DISABLE_DEMO_WINDOWS                        // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.\n//#define IMGUI_DISABLE_DEBUG_TOOLS                         // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.\n\n//---- Don't implement some functions to reduce linkage requirements.\n//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS   // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)\n//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS          // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)\n//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS         // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)\n//#define IMGUI_DISABLE_WIN32_FUNCTIONS                     // [Win32] Won't use and link with any Win32 function (clipboard, IME).\n//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS      // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).\n//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS            // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)\n//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS              // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.\n//#define IMGUI_DISABLE_FILE_FUNCTIONS                      // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)\n//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS              // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.\n//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS                  // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().\n//#define IMGUI_DISABLE_SSE                                 // Disable use of SSE intrinsics even if available\n\n//---- Include imgui_user.h at the end of imgui.h as a convenience\n//#define IMGUI_INCLUDE_IMGUI_USER_H\n\n//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)\n//#define IMGUI_USE_BGRA_PACKED_COLOR\n\n//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)\n//#define IMGUI_USE_WCHAR32\n\n//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version\n// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.\n//#define IMGUI_STB_TRUETYPE_FILENAME   \"my_folder/stb_truetype.h\"\n//#define IMGUI_STB_RECT_PACK_FILENAME  \"my_folder/stb_rect_pack.h\"\n//#define IMGUI_STB_SPRINTF_FILENAME    \"my_folder/stb_sprintf.h\"    // only used if IMGUI_USE_STB_SPRINTF is defined.\n//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION\n//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION\n//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION                   // only disabled if IMGUI_USE_STB_SPRINTF is defined.\n\n//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)\n// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.\n//#define IMGUI_USE_STB_SPRINTF\n\n//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)\n// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).\n// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.\n//#define IMGUI_ENABLE_FREETYPE\n\n//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)\n// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).\n// Only works in combination with IMGUI_ENABLE_FREETYPE.\n// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)\n//#define IMGUI_ENABLE_FREETYPE_LUNASVG\n\n//---- Use stb_truetype to build and rasterize the font atlas (default)\n// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.\n//#define IMGUI_ENABLE_STB_TRUETYPE\n\n//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.\n// This will be inlined as part of ImVec2 and ImVec4 class declarations.\n/*\n#define IM_VEC2_CLASS_EXTRA                                                     \\\n        constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {}                   \\\n        operator MyVec2() const { return MyVec2(x,y); }\n\n#define IM_VEC4_CLASS_EXTRA                                                     \\\n        constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {}   \\\n        operator MyVec4() const { return MyVec4(x,y,z,w); }\n*/\n//---- ...Or use Dear ImGui's own very basic math operators.\n//#define IMGUI_DEFINE_MATH_OPERATORS\n\n//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.\n// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).\n// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.\n// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.\n//#define ImDrawIdx unsigned int\n\n//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)\n//struct ImDrawList;\n//struct ImDrawCmd;\n//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);\n//#define ImDrawCallback MyImDrawCallback\n\n//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)\n// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)\n//#define IM_DEBUG_BREAK  IM_ASSERT(0)\n//#define IM_DEBUG_BREAK  __debugbreak()\n\n//---- Debug Tools: Enable slower asserts\n//#define IMGUI_DEBUG_PARANOID\n\n//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)\n/*\nnamespace ImGui\n{\n    void MyFunction(const char* name, MyMatrix44* mtx);\n}\n*/\n"
  },
  {
    "path": "third-party/imgui/imgui.cpp",
    "content": "// dear imgui, v1.90.0\n// (main code and documentation)\n\n// Help:\n// - See links below.\n// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.\n// - Read top of imgui.cpp for more details, links and comments.\n\n// Resources:\n// - FAQ                   https://dearimgui.com/faq\n// - Getting Started       https://dearimgui.com/getting-started\n// - Homepage              https://github.com/ocornut/imgui\n// - Releases & changelog  https://github.com/ocornut/imgui/releases\n// - Gallery               https://github.com/ocornut/imgui/issues/6897 (please post your screenshots/video there!)\n// - Wiki                  https://github.com/ocornut/imgui/wiki (lots of good stuff there)\n// - Glossary              https://github.com/ocornut/imgui/wiki/Glossary\n// - Issues & support      https://github.com/ocornut/imgui/issues\n// - Tests & Automation    https://github.com/ocornut/imgui_test_engine\n\n// For first-time users having issues compiling/linking/running/loading fonts:\n// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.\n// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.\n\n// Copyright (c) 2014-2023 Omar Cornut\n// Developed by Omar Cornut and every direct or indirect contributors to the GitHub.\n// See LICENSE.txt for copyright and licensing details (standard MIT License).\n// This library is free but needs your support to sustain development and maintenance.\n// Businesses: you can support continued development via B2B invoiced technical support, maintenance and sponsoring contracts.\n// PLEASE reach out at omar AT dearimgui DOT com. See https://github.com/ocornut/imgui/wiki/Sponsors\n// Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.\n\n// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library.\n// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without\n// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't\n// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you\n// to a better solution or official support for them.\n\n/*\n\nIndex of this file:\n\nDOCUMENTATION\n\n- MISSION STATEMENT\n- CONTROLS GUIDE\n- PROGRAMMER GUIDE\n  - READ FIRST\n  - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI\n  - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE\n  - HOW A SIMPLE APPLICATION MAY LOOK LIKE\n  - HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE\n- API BREAKING CHANGES (read me when you update!)\n- FREQUENTLY ASKED QUESTIONS (FAQ)\n  - Read all answers online: https://www.dearimgui.com/faq, or in docs/FAQ.md (with a Markdown viewer)\n\nCODE\n(search for \"[SECTION]\" in the code to find them)\n\n// [SECTION] INCLUDES\n// [SECTION] FORWARD DECLARATIONS\n// [SECTION] CONTEXT AND MEMORY ALLOCATORS\n// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)\n// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)\n// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)\n// [SECTION] MISC HELPERS/UTILITIES (File functions)\n// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)\n// [SECTION] MISC HELPERS/UTILITIES (Color functions)\n// [SECTION] ImGuiStorage\n// [SECTION] ImGuiTextFilter\n// [SECTION] ImGuiTextBuffer, ImGuiTextIndex\n// [SECTION] ImGuiListClipper\n// [SECTION] STYLING\n// [SECTION] RENDER HELPERS\n// [SECTION] INITIALIZATION, SHUTDOWN\n// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)\n// [SECTION] INPUTS\n// [SECTION] ERROR CHECKING\n// [SECTION] LAYOUT\n// [SECTION] SCROLLING\n// [SECTION] TOOLTIPS\n// [SECTION] POPUPS\n// [SECTION] KEYBOARD/GAMEPAD NAVIGATION\n// [SECTION] DRAG AND DROP\n// [SECTION] LOGGING/CAPTURING\n// [SECTION] SETTINGS\n// [SECTION] LOCALIZATION\n// [SECTION] VIEWPORTS, PLATFORM WINDOWS\n// [SECTION] PLATFORM DEPENDENT HELPERS\n// [SECTION] METRICS/DEBUGGER WINDOW\n// [SECTION] DEBUG LOG WINDOW\n// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)\n\n*/\n\n//-----------------------------------------------------------------------------\n// DOCUMENTATION\n//-----------------------------------------------------------------------------\n\n/*\n\n MISSION STATEMENT\n =================\n\n - Easy to use to create code-driven and data-driven tools.\n - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools.\n - Easy to hack and improve.\n - Minimize setup and maintenance.\n - Minimize state storage on user side.\n - Minimize state synchronization.\n - Portable, minimize dependencies, run on target (consoles, phones, etc.).\n - Efficient runtime and memory consumption.\n\n Designed primarily for developers and content-creators, not the typical end-user!\n Some of the current weaknesses (which we aim to address in the future) includes:\n\n - Doesn't look fancy.\n - Limited layout features, intricate layouts are typically crafted in code.\n\n\n CONTROLS GUIDE\n ==============\n\n - MOUSE CONTROLS\n   - Mouse wheel:                   Scroll vertically.\n   - SHIFT+Mouse wheel:             Scroll horizontally.\n   - Click [X]:                     Close a window, available when 'bool* p_open' is passed to ImGui::Begin().\n   - Click ^, Double-Click title:   Collapse window.\n   - Drag on corner/border:         Resize window (double-click to auto fit window to its contents).\n   - Drag on any empty space:       Move window (unless io.ConfigWindowsMoveFromTitleBarOnly = true).\n   - Left-click outside popup:      Close popup stack (right-click over underlying popup: Partially close popup stack).\n\n - TEXT EDITOR\n   - Hold SHIFT or Drag Mouse:      Select text.\n   - CTRL+Left/Right:               Word jump.\n   - CTRL+Shift+Left/Right:         Select words.\n   - CTRL+A or Double-Click:        Select All.\n   - CTRL+X, CTRL+C, CTRL+V:        Use OS clipboard.\n   - CTRL+Z, CTRL+Y:                Undo, Redo.\n   - ESCAPE:                        Revert text to its original value.\n   - On OSX, controls are automatically adjusted to match standard OSX text editing shortcuts and behaviors.\n\n - KEYBOARD CONTROLS\n   - Basic:\n     - Tab, SHIFT+Tab               Cycle through text editable fields.\n     - CTRL+Tab, CTRL+Shift+Tab     Cycle through windows.\n     - CTRL+Click                   Input text into a Slider or Drag widget.\n   - Extended features with `io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard`:\n     - Tab, SHIFT+Tab:              Cycle through every items.\n     - Arrow keys                   Move through items using directional navigation. Tweak value.\n     - Arrow keys + Alt, Shift      Tweak slower, tweak faster (when using arrow keys).\n     - Enter                        Activate item (prefer text input when possible).\n     - Space                        Activate item (prefer tweaking with arrows when possible).\n     - Escape                       Deactivate item, leave child window, close popup.\n     - Page Up, Page Down           Previous page, next page.\n     - Home, End                    Scroll to top, scroll to bottom.\n     - Alt                          Toggle between scrolling layer and menu layer.\n     - CTRL+Tab then Ctrl+Arrows    Move window. Hold SHIFT to resize instead of moving.\n   - Output when ImGuiConfigFlags_NavEnableKeyboard set,\n     - io.WantCaptureKeyboard flag is set when keyboard is claimed.\n     - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set.\n     - io.NavVisible: true when the navigation cursor is visible (usually goes to back false when mouse is used).\n\n - GAMEPAD CONTROLS\n   - Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.\n   - Particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse!\n   - Download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets\n   - Backend support: backend needs to:\n      - Set 'io.BackendFlags |= ImGuiBackendFlags_HasGamepad' + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys.\n      - For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly.\n        Backend code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.).\n      - BEFORE 1.87, BACKENDS USED TO WRITE TO io.NavInputs[]. This is now obsolete. Please call io functions instead!\n   - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing,\n     with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved.\n\n - REMOTE INPUTS SHARING & MOUSE EMULATION\n   - PS4/PS5 users: Consider emulating a mouse cursor with DualShock touch pad or a spare analog stick as a mouse-emulation fallback.\n   - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + run examples/libs/synergy/uSynergy.c (on your console/tablet/phone app)\n     in order to share your PC mouse/keyboard.\n   - See https://github.com/ocornut/imgui/wiki/Useful-Extensions#remoting for other remoting solutions.\n   - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag.\n     Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs Dear ImGui to move your mouse cursor along with navigation movements.\n     When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved.\n     When that happens your backend NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the backends in examples/ do that.\n     (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, Dear ImGui will misbehave as it will see your mouse moving back & forth!)\n     (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want\n     to set a boolean to ignore your other external mouse positions until the external source is moved again.)\n\n\n PROGRAMMER GUIDE\n ================\n\n READ FIRST\n ----------\n - Remember to check the wonderful Wiki (https://github.com/ocornut/imgui/wiki)\n - Your code creates the UI every frame of your application loop, if your code doesn't run the UI is gone!\n   The UI can be highly dynamic, there are no construction or destruction steps, less superfluous\n   data retention on your side, less state duplication, less state synchronization, fewer bugs.\n - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features.\n   Or browse https://pthom.github.io/imgui_manual_online/manual/imgui_manual.html for interactive web version.\n - The library is designed to be built from sources. Avoid pre-compiled binaries and packaged versions. See imconfig.h to configure your build.\n - Dear ImGui is an implementation of the IMGUI paradigm (immediate-mode graphical user interface, a term coined by Casey Muratori).\n   You can learn about IMGUI principles at http://www.johno.se/book/imgui.html, http://mollyrocket.com/861 & more links in Wiki.\n - Dear ImGui is a \"single pass\" rasterizing implementation of the IMGUI paradigm, aimed at ease of use and high-performances.\n   For every application frame, your UI code will be called only once. This is in contrast to e.g. Unity's implementation of an IMGUI,\n   where the UI code is called multiple times (\"multiple passes\") from a single entry point. There are pros and cons to both approaches.\n - Our origin is on the top-left. In axis aligned bounding boxes, Min = top-left, Max = bottom-right.\n - Please make sure you have asserts enabled (IM_ASSERT redirects to assert() by default, but can be redirected).\n   If you get an assert, read the messages and comments around the assert.\n - This codebase aims to be highly optimized:\n   - A typical idle frame should never call malloc/free.\n   - We rely on a maximum of constant-time or O(N) algorithms. Limiting searches/scans as much as possible.\n   - We put particular energy in making sure performances are decent with typical \"Debug\" build settings as well.\n     Which mean we tend to avoid over-relying on \"zero-cost abstraction\" as they aren't zero-cost at all.\n - This codebase aims to be both highly opinionated and highly flexible:\n   - This code works because of the things it choose to solve or not solve.\n   - C++: this is a pragmatic C-ish codebase: we don't use fancy C++ features, we don't include C++ headers,\n     and ImGui:: is a namespace. We rarely use member functions (and when we did, I am mostly regretting it now).\n     This is to increase compatibility, increase maintainability and facilitate use from other languages.\n   - C++: ImVec2/ImVec4 do not expose math operators by default, because it is expected that you use your own math types.\n     See FAQ \"How can I use my own math types instead of ImVec2/ImVec4?\" for details about setting up imconfig.h for that.\n     We can can optionally export math operators for ImVec2/ImVec4 using IMGUI_DEFINE_MATH_OPERATORS, which we use internally.\n   - C++: pay attention that ImVector<> manipulates plain-old-data and does not honor construction/destruction\n     (so don't use ImVector in your code or at our own risk!).\n   - Building: We don't use nor mandate a build system for the main library.\n     This is in an effort to ensure that it works in the real world aka with any esoteric build setup.\n     This is also because providing a build system for the main library would be of little-value.\n     The build problems are almost never coming from the main library but from specific backends.\n\n\n HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI\n ----------------------------------------------\n - Update submodule or copy/overwrite every file.\n - About imconfig.h:\n   - You may modify your copy of imconfig.h, in this case don't overwrite it.\n   - or you may locally branch to modify imconfig.h and merge/rebase latest.\n   - or you may '#define IMGUI_USER_CONFIG \"my_config_file.h\"' globally from your build system to\n     specify a custom path for your imconfig.h file and instead not have to modify the default one.\n\n - Overwrite all the sources files except for imconfig.h (if you have modified your copy of imconfig.h)\n - Or maintain your own branch where you have imconfig.h modified as a top-most commit which you can regularly rebase over \"master\".\n - You can also use '#define IMGUI_USER_CONFIG \"my_config_file.h\" to redirect configuration to your own file.\n - Read the \"API BREAKING CHANGES\" section (below). This is where we list occasional API breaking changes.\n   If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed\n   from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will\n   likely be a comment about it. Please report any issue to the GitHub page!\n - To find out usage of old API, you can add '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in your configuration file.\n - Try to keep your copy of Dear ImGui reasonably up to date!\n\n\n GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE\n ---------------------------------------------------------------\n - See https://github.com/ocornut/imgui/wiki/Getting-Started.\n - Run and study the examples and demo in imgui_demo.cpp to get acquainted with the library.\n - In the majority of cases you should be able to use unmodified backends files available in the backends/ folder.\n - Add the Dear ImGui source files + selected backend source files to your projects or using your preferred build system.\n   It is recommended you build and statically link the .cpp files as part of your project and NOT as a shared library (DLL).\n - You can later customize the imconfig.h file to tweak some compile-time behavior, such as integrating Dear ImGui types with your own maths types.\n - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them.\n - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide.\n   Effectively it means you can create widgets at any time in your code, regardless of considerations of being in \"update\" vs \"render\"\n   phases of your own application. All rendering information is stored into command-lists that you will retrieve after calling ImGui::Render().\n - Refer to the backends and demo applications in the examples/ folder for instruction on how to setup your code.\n - If you are running over a standard OS with a common graphics API, you should be able to use unmodified imgui_impl_*** files from the examples/ folder.\n\n\n HOW A SIMPLE APPLICATION MAY LOOK LIKE\n --------------------------------------\n EXHIBIT 1: USING THE EXAMPLE BACKENDS (= imgui_impl_XXX.cpp files from the backends/ folder).\n The sub-folders in examples/ contain examples applications following this structure.\n\n     // Application init: create a dear imgui context, setup some options, load fonts\n     ImGui::CreateContext();\n     ImGuiIO& io = ImGui::GetIO();\n     // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.\n     // TODO: Fill optional fields of the io structure later.\n     // TODO: Load TTF/OTF fonts if you don't want to use the default font.\n\n     // Initialize helper Platform and Renderer backends (here we are using imgui_impl_win32.cpp and imgui_impl_dx11.cpp)\n     ImGui_ImplWin32_Init(hwnd);\n     ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);\n\n     // Application main loop\n     while (true)\n     {\n         // Feed inputs to dear imgui, start new frame\n         ImGui_ImplDX11_NewFrame();\n         ImGui_ImplWin32_NewFrame();\n         ImGui::NewFrame();\n\n         // Any application code here\n         ImGui::Text(\"Hello, world!\");\n\n         // Render dear imgui into screen\n         ImGui::Render();\n         ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());\n         g_pSwapChain->Present(1, 0);\n     }\n\n     // Shutdown\n     ImGui_ImplDX11_Shutdown();\n     ImGui_ImplWin32_Shutdown();\n     ImGui::DestroyContext();\n\n EXHIBIT 2: IMPLEMENTING CUSTOM BACKEND / CUSTOM ENGINE\n\n     // Application init: create a dear imgui context, setup some options, load fonts\n     ImGui::CreateContext();\n     ImGuiIO& io = ImGui::GetIO();\n     // TODO: Set optional io.ConfigFlags values, e.g. 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard' to enable keyboard controls.\n     // TODO: Fill optional fields of the io structure later.\n     // TODO: Load TTF/OTF fonts if you don't want to use the default font.\n\n     // Build and load the texture atlas into a texture\n     // (In the examples/ app this is usually done within the ImGui_ImplXXX_Init() function from one of the demo Renderer)\n     int width, height;\n     unsigned char* pixels = nullptr;\n     io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);\n\n     // At this point you've got the texture data and you need to upload that to your graphic system:\n     // After we have created the texture, store its pointer/identifier (_in whichever format your engine uses_) in 'io.Fonts->TexID'.\n     // This will be passed back to your via the renderer. Basically ImTextureID == void*. Read FAQ for details about ImTextureID.\n     MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32)\n     io.Fonts->SetTexID((void*)texture);\n\n     // Application main loop\n     while (true)\n     {\n        // Setup low-level inputs, e.g. on Win32: calling GetKeyboardState(), or write to those fields from your Windows message handlers, etc.\n        // (In the examples/ app this is usually done within the ImGui_ImplXXX_NewFrame() function from one of the demo Platform Backends)\n        io.DeltaTime = 1.0f/60.0f;              // set the time elapsed since the previous frame (in seconds)\n        io.DisplaySize.x = 1920.0f;             // set the current display width\n        io.DisplaySize.y = 1280.0f;             // set the current display height here\n        io.AddMousePosEvent(mouse_x, mouse_y);  // update mouse position\n        io.AddMouseButtonEvent(0, mouse_b[0]);  // update mouse button states\n        io.AddMouseButtonEvent(1, mouse_b[1]);  // update mouse button states\n\n        // Call NewFrame(), after this point you can use ImGui::* functions anytime\n        // (So you want to try calling NewFrame() as early as you can in your main loop to be able to use Dear ImGui everywhere)\n        ImGui::NewFrame();\n\n        // Most of your application code here\n        ImGui::Text(\"Hello, world!\");\n        MyGameUpdate(); // may use any Dear ImGui functions, e.g. ImGui::Begin(\"My window\"); ImGui::Text(\"Hello, world!\"); ImGui::End();\n        MyGameRender(); // may use any Dear ImGui functions as well!\n\n        // Render dear imgui, swap buffers\n        // (You want to try calling EndFrame/Render as late as you can, to be able to use Dear ImGui in your own game rendering code)\n        ImGui::EndFrame();\n        ImGui::Render();\n        ImDrawData* draw_data = ImGui::GetDrawData();\n        MyImGuiRenderFunction(draw_data);\n        SwapBuffers();\n     }\n\n     // Shutdown\n     ImGui::DestroyContext();\n\n To decide whether to dispatch mouse/keyboard inputs to Dear ImGui to the rest of your application,\n you should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!\n Please read the FAQ entry \"How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?\" about this.\n\n\n HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE\n ---------------------------------------------\n The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function.\n\n    void MyImGuiRenderFunction(ImDrawData* draw_data)\n    {\n       // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled\n       // TODO: Setup texture sampling state: sample with bilinear filtering (NOT point/nearest filtering). Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering.\n       // TODO: Setup viewport covering draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize\n       // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize\n       // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color.\n       ImVec2 clip_off = draw_data->DisplayPos;\n       for (int n = 0; n < draw_data->CmdListsCount; n++)\n       {\n          const ImDrawList* cmd_list = draw_data->CmdLists[n];\n          const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;  // vertex buffer generated by Dear ImGui\n          const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;   // index buffer generated by Dear ImGui\n          for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)\n          {\n             const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];\n             if (pcmd->UserCallback)\n             {\n                 pcmd->UserCallback(cmd_list, pcmd);\n             }\n             else\n             {\n                 // Project scissor/clipping rectangles into framebuffer space\n                 ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);\n                 ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);\n                 if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)\n                     continue;\n\n                 // We are using scissoring to clip some objects. All low-level graphics API should support it.\n                 // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches\n                 //   (some elements visible outside their bounds) but you can fix that once everything else works!\n                 // - Clipping coordinates are provided in imgui coordinates space:\n                 //   - For a given viewport, draw_data->DisplayPos == viewport->Pos and draw_data->DisplaySize == viewport->Size\n                 //   - In a single viewport application, draw_data->DisplayPos == (0,0) and draw_data->DisplaySize == io.DisplaySize, but always use GetMainViewport()->Pos/Size instead of hardcoding those values.\n                 //   - In the interest of supporting multi-viewport applications (see 'docking' branch on github),\n                 //     always subtract draw_data->DisplayPos from clipping bounds to convert them to your viewport space.\n                 // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min)\n                 MyEngineSetScissor(clip_min.x, clip_min.y, clip_max.x, clip_max.y);\n\n                 // The texture for the draw call is specified by pcmd->GetTexID().\n                 // The vast majority of draw calls will use the Dear ImGui texture atlas, which value you have set yourself during initialization.\n                 MyEngineBindTexture((MyTexture*)pcmd->GetTexID());\n\n                 // Render 'pcmd->ElemCount/3' indexed triangles.\n                 // By default the indices ImDrawIdx are 16-bit, you can change them to 32-bit in imconfig.h if your engine doesn't support 16-bit indices.\n                 MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset, vtx_buffer, pcmd->VtxOffset);\n             }\n          }\n       }\n    }\n\n\n API BREAKING CHANGES\n ====================\n\n Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix.\n Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code.\n When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files.\n You can read releases logs https://github.com/ocornut/imgui/releases for more details.\n\n - 2023/11/09 (1.90.0) - removed IM_OFFSETOF() macro in favor of using offsetof() available in C++11. Kept redirection define (will obsolete).\n - 2023/11/07 (1.90.0) - removed BeginChildFrame()/EndChildFrame() in favor of using BeginChild() with the ImGuiChildFlags_FrameStyle flag. kept inline redirection function (will obsolete).\n                         those functions were merely PushStyle/PopStyle helpers, the removal isn't so much motivated by needing to add the feature in BeginChild(), but by the necessity to avoid BeginChildFrame() signature mismatching BeginChild() signature and features.\n - 2023/11/02 (1.90.0) - BeginChild: upgraded 'bool border = true' parameter to 'ImGuiChildFlags flags' type, added ImGuiChildFlags_Border equivalent. As with our prior \"bool-to-flags\" API updates, the ImGuiChildFlags_Border value is guaranteed to be == true forever to ensure a smoother transition, meaning all existing calls will still work.\n                           - old: BeginChild(\"Name\", size, true)\n                           - new: BeginChild(\"Name\", size, ImGuiChildFlags_Border)\n                           - old: BeginChild(\"Name\", size, false)\n                           - new: BeginChild(\"Name\", size) or BeginChild(\"Name\", 0) or BeginChild(\"Name\", size, ImGuiChildFlags_None)\n - 2023/11/02 (1.90.0) - BeginChild: added child-flag ImGuiChildFlags_AlwaysUseWindowPadding as a replacement for the window-flag ImGuiWindowFlags_AlwaysUseWindowPadding: the feature only ever made sense for BeginChild() anyhow.\n                           - old: BeginChild(\"Name\", size, 0, ImGuiWindowFlags_AlwaysUseWindowPadding);\n                           - new: BeginChild(\"Name\", size, ImGuiChildFlags_AlwaysUseWindowPadding, 0);\n - 2023/09/27 (1.90.0) - io: removed io.MetricsActiveAllocations introduced in 1.63. Same as 'g.DebugMemAllocCount - g.DebugMemFreeCount' (still displayed in Metrics, unlikely to be accessed by end-user).\n - 2023/09/26 (1.90.0) - debug tools: Renamed ShowStackToolWindow() (\"Stack Tool\") to ShowIDStackToolWindow() (\"ID Stack Tool\"), as earlier name was misleading. Kept inline redirection function. (#4631)\n - 2023/09/15 (1.90.0) - ListBox, Combo: changed signature of \"name getter\" callback in old one-liner ListBox()/Combo() apis. kept inline redirection function (will obsolete).\n                           - old: bool Combo(const char* label, int* current_item, bool (*getter)(void* user_data, int idx, const char** out_text), ...)\n                           - new: bool Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);\n                           - old: bool ListBox(const char* label, int* current_item, bool (*getting)(void* user_data, int idx, const char** out_text), ...);\n                           - new: bool ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), ...);\n - 2023/09/08 (1.90.0) - commented out obsolete redirecting functions:\n                           - GetWindowContentRegionWidth()  -> use GetWindowContentRegionMax().x - GetWindowContentRegionMin().x. Consider that generally 'GetContentRegionAvail().x' is more useful.\n                           - ImDrawCornerFlags_XXX          -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details + grep commented names in sources.\n                       - commented out runtime support for hardcoded ~0 or 0x01..0x0F rounding flags values for AddRect()/AddRectFilled()/PathRect()/AddImageRounded() -> use ImDrawFlags_RoundCornersXXX flags. Read 1.82 Changelog for details\n - 2023/08/25 (1.89.9) - clipper: Renamed IncludeRangeByIndices() (also called ForceDisplayRangeByIndices() before 1.89.6) to IncludeItemsByIndex(). Kept inline redirection function. Sorry!\n - 2023/07/12 (1.89.8) - ImDrawData: CmdLists now owned, changed from ImDrawList** to ImVector<ImDrawList*>. Majority of users shouldn't be affected, but you cannot compare to NULL nor reassign manually anymore. Instead use AddDrawList(). (#6406, #4879, #1878)\n - 2023/06/28 (1.89.7) - overlapping items: obsoleted 'SetItemAllowOverlap()' (called after item) in favor of calling 'SetNextItemAllowOverlap()' (called before item). 'SetItemAllowOverlap()' didn't and couldn't work reliably since 1.89 (2022-11-15).\n - 2023/06/28 (1.89.7) - overlapping items: renamed 'ImGuiTreeNodeFlags_AllowItemOverlap' to 'ImGuiTreeNodeFlags_AllowOverlap', 'ImGuiSelectableFlags_AllowItemOverlap' to 'ImGuiSelectableFlags_AllowOverlap'. Kept redirecting enums (will obsolete).\n - 2023/06/28 (1.89.7) - overlapping items: IsItemHovered() now by default return false when querying an item using AllowOverlap mode which is being overlapped. Use ImGuiHoveredFlags_AllowWhenOverlappedByItem to revert to old behavior.\n - 2023/06/28 (1.89.7) - overlapping items: Selectable and TreeNode don't allow overlap when active so overlapping widgets won't appear as hovered. While this fixes a common small visual issue, it also means that calling IsItemHovered() after a non-reactive elements - e.g. Text() - overlapping an active one may fail if you don't use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem). (#6610)\n - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage.\n - 2023/05/30 (1.89.6) - backends: renamed \"imgui_impl_sdlrenderer.cpp\" to \"imgui_impl_sdlrenderer2.cpp\" and \"imgui_impl_sdlrenderer.h\" to \"imgui_impl_sdlrenderer2.h\". This is in prevision for the future release of SDL3.\n - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:\n                           - ListBoxHeader()  -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)\n                           - ListBoxFooter()  -> use EndListBox()\n - 2023/05/15 (1.89.6) - clipper: commented out obsolete redirection constructor 'ImGuiListClipper(int items_count, float items_height = -1.0f)' that was marked obsolete in 1.79. Use default constructor + clipper.Begin().\n - 2023/05/15 (1.89.6) - clipper: renamed ImGuiListClipper::ForceDisplayRangeByIndices() to ImGuiListClipper::IncludeRangeByIndices().\n - 2023/03/14 (1.89.4) - commented out redirecting enums/functions names that were marked obsolete two years ago:\n                           - ImGuiSliderFlags_ClampOnInput        -> use ImGuiSliderFlags_AlwaysClamp\n                           - ImGuiInputTextFlags_AlwaysInsertMode -> use ImGuiInputTextFlags_AlwaysOverwrite\n                           - ImDrawList::AddBezierCurve()         -> use ImDrawList::AddBezierCubic()\n                           - ImDrawList::PathBezierCurveTo()      -> use ImDrawList::PathBezierCubicCurveTo()\n - 2023/03/09 (1.89.4) - renamed PushAllowKeyboardFocus()/PopAllowKeyboardFocus() to PushTabStop()/PopTabStop(). Kept inline redirection functions (will obsolete).\n - 2023/03/09 (1.89.4) - tooltips: Added 'bool' return value to BeginTooltip() for API consistency. Please only submit contents and call EndTooltip() if BeginTooltip() returns true. In reality the function will _currently_ always return true, but further changes down the line may change this, best to clarify API sooner.\n - 2023/02/15 (1.89.4) - moved the optional \"courtesy maths operators\" implementation from imgui_internal.h in imgui.h.\n                         Even though we encourage using your own maths types and operators by setting up IM_VEC2_CLASS_EXTRA,\n                         it has been frequently requested by people to use our own. We had an opt-in define which was\n                         previously fulfilled in imgui_internal.h. It is now fulfilled in imgui.h. (#6164)\n                           - OK:     #define IMGUI_DEFINE_MATH_OPERATORS / #include \"imgui.h\" / #include \"imgui_internal.h\"\n                           - Error:  #include \"imgui.h\" / #define IMGUI_DEFINE_MATH_OPERATORS / #include \"imgui_internal.h\"\n - 2023/02/07 (1.89.3) - backends: renamed \"imgui_impl_sdl.cpp\" to \"imgui_impl_sdl2.cpp\" and \"imgui_impl_sdl.h\" to \"imgui_impl_sdl2.h\". (#6146) This is in prevision for the future release of SDL3.\n - 2022/10/26 (1.89)   - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79.\n - 2022/10/12 (1.89)   - removed runtime patching of invalid \"%f\"/\"%0.f\" format strings for DragInt()/SliderInt(). This was obsoleted in 1.61 (May 2018). See 1.61 changelog for details.\n - 2022/09/26 (1.89)   - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete).\n                           - ImGuiKey_ModCtrl  and ImGuiModFlags_Ctrl  -> ImGuiMod_Ctrl\n                           - ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift\n                           - ImGuiKey_ModAlt   and ImGuiModFlags_Alt   -> ImGuiMod_Alt\n                           - ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super\n                         the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends.\n                         the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions.\n                         exceptionally commenting out the older ImGuiKeyModFlags_XXX names ahead of obsolescence schedule to reduce confusion and because they were not meant to be used anyway.\n - 2022/09/20 (1.89)   - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers.\n                         this will require uses of legacy backend-dependent indices to be casted, e.g.\n                            - with imgui_impl_glfw:  IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A);\n                            - with imgui_impl_win32: IsKeyPressed('A')        -> IsKeyPressed((ImGuiKey)'A')\n                            - etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now!\n - 2022/09/12 (1.89) - removed the bizarre legacy default argument for 'TreePush(const void* ptr = NULL)', always pass a pointer value explicitly. NULL/nullptr is ok but require cast, e.g. TreePush((void*)nullptr);\n - 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020):\n                         - DragScalar(), DragScalarN(), DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.\n                         - SliderScalar(), SliderScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f.\n                         - BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags)\n - 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries.\n                       this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item.\n                         - previously this would make the window content size ~200x200:\n                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();\n                         - instead, please submit an item:\n                              Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End();\n                         - alternative:\n                              Begin(...) + Dummy(ImVec2(200,200)) + End();\n                         - content size is now only extended when submitting an item!\n                         - with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert.\n                         - without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it.\n - 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete).\n                        - added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter.\n                        - old signature: bool ImageButton(ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), int frame_padding = -1, ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));\n                          - used the ImTextureID value to create an ID. This was inconsistent with other functions, led to ID conflicts, and caused problems with engines using transient ImTextureID values.\n                          - had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer.\n                        - new signature: bool ImageButton(const char* str_id, ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1));\n                          - requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier.\n                          - always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this.\n - 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes).\n                        - Official backends from 1.87+                  -> no issue.\n                        - Official backends from 1.60 to 1.86           -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating!\n                        - Custom backends not writing to io.NavInputs[] -> no issue.\n                        - Custom backends writing to io.NavInputs[]     -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing!\n                        - TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[].\n - 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete).\n - 2022/05/03 (1.88) - backends: osx: removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. All ImGui_ImplOSX_HandleEvent() calls should be removed as they are now unnecessary.\n - 2022/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO.\n - 2022/01/20 (1.87) - inputs: reworded gamepad IO.\n                        - Backend writing to io.NavInputs[]            -> backend should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values.\n - 2022/01/19 (1.87) - sliders, drags: removed support for legacy arithmetic operators (+,+-,*,/) when inputing text. This doesn't break any api/code but a feature that used to be accessible by end-users (which seemingly no one used).\n - 2022/01/17 (1.87) - inputs: reworked mouse IO.\n                        - Backend writing to io.MousePos               -> backend should call io.AddMousePosEvent()\n                        - Backend writing to io.MouseDown[]            -> backend should call io.AddMouseButtonEvent()\n                        - Backend writing to io.MouseWheel             -> backend should call io.AddMouseWheelEvent()\n                        - Backend writing to io.MouseHoveredViewport   -> backend should call io.AddMouseViewportEvent() [Docking branch w/ multi-viewports only]\n                       note: for all calls to IO new functions, the Dear ImGui context should be bound/current.\n                       read https://github.com/ocornut/imgui/issues/4921 for details.\n - 2022/01/10 (1.87) - inputs: reworked keyboard IO. Removed io.KeyMap[], io.KeysDown[] in favor of calling io.AddKeyEvent(). Removed GetKeyIndex(), now unecessary. All IsKeyXXX() functions now take ImGuiKey values. All features are still functional until IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Read Changelog and Release Notes for details.\n                        - IsKeyPressed(MY_NATIVE_KEY_XXX)              -> use IsKeyPressed(ImGuiKey_XXX)\n                        - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX))      -> use IsKeyPressed(ImGuiKey_XXX)\n                        - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to stil function with legacy key codes).\n                        - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiMod_XXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiMod_XXX values.*\n                     - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. \"io.KeyMap[ImGuiKey_A] = ImGuiKey_A\") because those values are now larger than the legacy KeyDown[] array. Will assert.\n                     - inputs: added ImGuiKey_ModCtrl/ImGuiKey_ModShift/ImGuiKey_ModAlt/ImGuiKey_ModSuper values to submit keyboard modifiers using io.AddKeyEvent(), instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper.\n - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum.\n - 2022/01/05 (1.87) - removed io.ImeSetInputScreenPosFn() in favor of more flexible io.SetPlatformImeDataFn(). Removed 'void* io.ImeWindowHandle' in favor of writing to 'void* ImGuiViewport::PlatformHandleRaw'.\n - 2022/01/01 (1.87) - commented out redirecting functions/enums names that were marked obsolete in 1.69, 1.70, 1.71, 1.72 (March-July 2019)\n                        - ImGui::SetNextTreeNodeOpen()        -> use ImGui::SetNextItemOpen()\n                        - ImGui::GetContentRegionAvailWidth() -> use ImGui::GetContentRegionAvail().x\n                        - ImGui::TreeAdvanceToLabelPos()      -> use ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());\n                        - ImFontAtlas::CustomRect             -> use ImFontAtlasCustomRect\n                        - ImGuiColorEditFlags_RGB/HSV/HEX     -> use ImGuiColorEditFlags_DisplayRGB/HSV/Hex\n - 2021/12/20 (1.86) - backends: removed obsolete Marmalade backend (imgui_impl_marmalade.cpp) + example. Find last supported version at https://github.com/ocornut/imgui/wiki/Bindings\n - 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function.\n - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful.\n - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):\n                        - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()\n                        - ImFont::GlyphRangesBuilder  -> use ImFontGlyphRangesBuilder\n - 2021/05/19 (1.83) - backends: obsoleted direct access to ImDrawCmd::TextureId in favor of calling ImDrawCmd::GetTexID().\n                        - if you are using official backends from the source tree: you have nothing to do.\n                        - if you have copied old backend code or using your own: change access to draw_cmd->TextureId to draw_cmd->GetTexID().\n - 2021/03/12 (1.82) - upgraded ImDrawList::AddRect(), AddRectFilled(), PathRect() to use ImDrawFlags instead of ImDrawCornersFlags.\n                        - ImDrawCornerFlags_TopLeft  -> use ImDrawFlags_RoundCornersTopLeft\n                        - ImDrawCornerFlags_BotRight -> use ImDrawFlags_RoundCornersBottomRight\n                        - ImDrawCornerFlags_None     -> use ImDrawFlags_RoundCornersNone etc.\n                       flags now sanely defaults to 0 instead of 0x0F, consistent with all other flags in the API.\n                       breaking: the default with rounding > 0.0f is now \"round all corners\" vs old implicit \"round no corners\":\n                        - rounding == 0.0f + flags == 0 --> meant no rounding  --> unchanged (common use)\n                        - rounding  > 0.0f + flags != 0 --> meant rounding     --> unchanged (common use)\n                        - rounding == 0.0f + flags != 0 --> meant no rounding  --> unchanged (unlikely use)\n                        - rounding  > 0.0f + flags == 0 --> meant no rounding  --> BREAKING (unlikely use): will now round all corners --> use ImDrawFlags_RoundCornersNone or rounding == 0.0f.\n                       this ONLY matters for hard coded use of 0 + rounding > 0.0f. Use of named ImDrawFlags_RoundCornersNone (new) or ImDrawCornerFlags_None (old) are ok.\n                       the old ImDrawCornersFlags used awkward default values of ~0 or 0xF (4 lower bits set) to signify \"round all corners\" and we sometimes encouraged using them as shortcuts.\n                       legacy path still support use of hard coded ~0 or any value from 0x1 or 0xF. They will behave the same with legacy paths enabled (will assert otherwise).\n - 2021/03/11 (1.82) - removed redirecting functions/enums names that were marked obsolete in 1.66 (September 2018):\n                        - ImGui::SetScrollHere()              -> use ImGui::SetScrollHereY()\n - 2021/03/11 (1.82) - clarified that ImDrawList::PathArcTo(), ImDrawList::PathArcToFast() won't render with radius < 0.0f. Previously it sorts of accidentally worked but would generally lead to counter-clockwise paths and have an effect on anti-aliasing.\n - 2021/03/10 (1.82) - upgraded ImDrawList::AddPolyline() and PathStroke() \"bool closed\" parameter to \"ImDrawFlags flags\". The matching ImDrawFlags_Closed value is guaranteed to always stay == 1 in the future.\n - 2021/02/22 (1.82) - (*undone in 1.84*) win32+mingw: Re-enabled IME functions by default even under MinGW. In July 2016, issue #738 had me incorrectly disable those default functions for MinGW. MinGW users should: either link with -limm32, either set their imconfig file  with '#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS'.\n - 2021/02/17 (1.82) - renamed rarely used style.CircleSegmentMaxError (old default = 1.60f) to style.CircleTessellationMaxError (new default = 0.30f) as the meaning of the value changed.\n - 2021/02/03 (1.81) - renamed ListBoxHeader(const char* label, ImVec2 size) to BeginListBox(). Kept inline redirection function (will obsolete).\n                     - removed ListBoxHeader(const char* label, int items_count, int height_in_items = -1) in favor of specifying size. Kept inline redirection function (will obsolete).\n                     - renamed ListBoxFooter() to EndListBox(). Kept inline redirection function (will obsolete).\n - 2021/01/26 (1.81) - removed ImGuiFreeType::BuildFontAtlas(). Kept inline redirection function. Prefer using '#define IMGUI_ENABLE_FREETYPE', but there's a runtime selection path available too. The shared extra flags parameters (very rarely used) are now stored in ImFontAtlas::FontBuilderFlags.\n                     - renamed ImFontConfig::RasterizerFlags (used by FreeType) to ImFontConfig::FontBuilderFlags.\n                     - renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API.\n - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.63 (August 2018):\n                        - ImGui::IsItemDeactivatedAfterChange() -> use ImGui::IsItemDeactivatedAfterEdit().\n                        - ImGuiCol_ModalWindowDarkening       -> use ImGuiCol_ModalWindowDimBg\n                        - ImGuiInputTextCallback              -> use ImGuiTextEditCallback\n                        - ImGuiInputTextCallbackData          -> use ImGuiTextEditCallbackData\n - 2020/12/21 (1.80) - renamed ImDrawList::AddBezierCurve() to AddBezierCubic(), and PathBezierCurveTo() to PathBezierCubicCurveTo(). Kept inline redirection function (will obsolete).\n - 2020/12/04 (1.80) - added imgui_tables.cpp file! Manually constructed project files will need the new file added!\n - 2020/11/18 (1.80) - renamed undocumented/internals ImGuiColumnsFlags_* to ImGuiOldColumnFlags_* in prevision of incoming Tables API.\n - 2020/11/03 (1.80) - renamed io.ConfigWindowsMemoryCompactTimer to io.ConfigMemoryCompactTimer as the feature will apply to other data structures\n - 2020/10/14 (1.80) - backends: moved all backends files (imgui_impl_XXXX.cpp, imgui_impl_XXXX.h) from examples/ to backends/.\n - 2020/10/12 (1.80) - removed redirecting functions/enums that were marked obsolete in 1.60 (April 2018):\n                        - io.RenderDrawListsFn pointer        -> use ImGui::GetDrawData() value and call the render function of your backend\n                        - ImGui::IsAnyWindowFocused()         -> use ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)\n                        - ImGui::IsAnyWindowHovered()         -> use ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)\n                        - ImGuiStyleVar_Count_                -> use ImGuiStyleVar_COUNT\n                        - ImGuiMouseCursor_Count_             -> use ImGuiMouseCursor_COUNT\n                      - removed redirecting functions names that were marked obsolete in 1.61 (May 2018):\n                        - InputFloat (... int decimal_precision ...) -> use InputFloat (... const char* format ...) with format = \"%.Xf\" where X is your value for decimal_precision.\n                        - same for InputFloat2()/InputFloat3()/InputFloat4() variants taking a `int decimal_precision` parameter.\n - 2020/10/05 (1.79) - removed ImGuiListClipper: Renamed constructor parameters which created an ambiguous alternative to using the ImGuiListClipper::Begin() function, with misleading edge cases (note: imgui_memory_editor <0.40 from imgui_club/ used this old clipper API. Update your copy if needed).\n - 2020/09/25 (1.79) - renamed ImGuiSliderFlags_ClampOnInput to ImGuiSliderFlags_AlwaysClamp. Kept redirection enum (will obsolete sooner because previous name was added recently).\n - 2020/09/25 (1.79) - renamed style.TabMinWidthForUnselectedCloseButton to style.TabMinWidthForCloseButton.\n - 2020/09/21 (1.79) - renamed OpenPopupContextItem() back to OpenPopupOnItemClick(), reverting the change from 1.77. For varieties of reason this is more self-explanatory.\n - 2020/09/21 (1.79) - removed return value from OpenPopupOnItemClick() - returned true on mouse release on an item - because it is inconsistent with other popup APIs and makes others misleading. It's also and unnecessary: you can use IsWindowAppearing() after BeginPopup() for a similar result.\n - 2020/09/17 (1.79) - removed ImFont::DisplayOffset in favor of ImFontConfig::GlyphOffset. DisplayOffset was applied after scaling and not very meaningful/useful outside of being needed by the default ProggyClean font. If you scaled this value after calling AddFontDefault(), this is now done automatically. It was also getting in the way of better font scaling, so let's get rid of it now!\n - 2020/08/17 (1.78) - obsoleted use of the trailing 'float power=1.0f' parameter for DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(), DragFloatRange2(), DragScalar(), DragScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(), SliderScalar(), SliderScalarN(), VSliderFloat() and VSliderScalar().\n                       replaced the 'float power=1.0f' argument with integer-based flags defaulting to 0 (as with all our flags).\n                       worked out a backward-compatibility scheme so hopefully most C++ codebase should not be affected. in short, when calling those functions:\n                       - if you omitted the 'power' parameter (likely!), you are not affected.\n                       - if you set the 'power' parameter to 1.0f (same as previous default value): 1/ your compiler may warn on float>int conversion, 2/ everything else will work. 3/ you can replace the 1.0f value with 0 to fix the warning, and be technically correct.\n                       - if you set the 'power' parameter to >1.0f (to enable non-linear editing): 1/ your compiler may warn on float>int conversion, 2/ code will assert at runtime, 3/ in case asserts are disabled, the code will not crash and enable the _Logarithmic flag. 4/ you can replace the >1.0f value with ImGuiSliderFlags_Logarithmic to fix the warning/assert and get a _similar_ effect as previous uses of power >1.0f.\n                       see https://github.com/ocornut/imgui/issues/3361 for all details.\n                       kept inline redirection functions (will obsolete) apart for: DragFloatRange2(), VSliderFloat(), VSliderScalar(). For those three the 'float power=1.0f' version was removed directly as they were most unlikely ever used.\n                       for shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`.\n                     - obsoleted use of v_min > v_max in DragInt, DragFloat, DragScalar to lock edits (introduced in 1.73, was not demoed nor documented very), will be replaced by a more generic ReadOnly feature. You may use the ImGuiSliderFlags_ReadOnly internal flag in the meantime.\n - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems.\n - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). [NOTE: THIS WAS REVERTED IN 1.79]\n - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017.\n - 2020/04/23 (1.77) - removed unnecessary ID (first arg) of ImFontAtlas::AddCustomRectRegular().\n - 2020/01/22 (1.75) - ImDrawList::AddCircle()/AddCircleFilled() functions don't accept negative radius any more.\n - 2019/12/17 (1.75) - [undid this change in 1.76] made Columns() limited to 64 columns by asserting above that limit. While the current code technically supports it, future code may not so we're putting the restriction ahead.\n - 2019/12/13 (1.75) - [imgui_internal.h] changed ImRect() default constructor initializes all fields to 0.0f instead of (FLT_MAX,FLT_MAX,-FLT_MAX,-FLT_MAX). If you used ImRect::Add() to create bounding boxes by adding multiple points into it, you may need to fix your initial value.\n - 2019/12/08 (1.75) - removed redirecting functions/enums that were marked obsolete in 1.53 (December 2017):\n                       - ShowTestWindow()                    -> use ShowDemoWindow()\n                       - IsRootWindowFocused()               -> use IsWindowFocused(ImGuiFocusedFlags_RootWindow)\n                       - IsRootWindowOrAnyChildFocused()     -> use IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)\n                       - SetNextWindowContentWidth(w)        -> use SetNextWindowContentSize(ImVec2(w, 0.0f)\n                       - GetItemsLineHeightWithSpacing()     -> use GetFrameHeightWithSpacing()\n                       - ImGuiCol_ChildWindowBg              -> use ImGuiCol_ChildBg\n                       - ImGuiStyleVar_ChildWindowRounding   -> use ImGuiStyleVar_ChildRounding\n                       - ImGuiTreeNodeFlags_AllowOverlapMode -> use ImGuiTreeNodeFlags_AllowItemOverlap\n                       - IMGUI_DISABLE_TEST_WINDOWS          -> use IMGUI_DISABLE_DEMO_WINDOWS\n - 2019/12/08 (1.75) - obsoleted calling ImDrawList::PrimReserve() with a negative count (which was vaguely documented and rarely if ever used). Instead, we added an explicit PrimUnreserve() API.\n - 2019/12/06 (1.75) - removed implicit default parameter to IsMouseDragging(int button = 0) to be consistent with other mouse functions (none of the other functions have it).\n - 2019/11/21 (1.74) - ImFontAtlas::AddCustomRectRegular() now requires an ID larger than 0x110000 (instead of 0x10000) to conform with supporting Unicode planes 1-16 in a future update. ID below 0x110000 will now assert.\n - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS to IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS for consistency.\n - 2019/11/19 (1.74) - renamed IMGUI_DISABLE_MATH_FUNCTIONS to IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS for consistency.\n - 2019/10/22 (1.74) - removed redirecting functions/enums that were marked obsolete in 1.52 (October 2017):\n                       - Begin() [old 5 args version]        -> use Begin() [3 args], use SetNextWindowSize() SetNextWindowBgAlpha() if needed\n                       - IsRootWindowOrAnyChildHovered()     -> use IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows)\n                       - AlignFirstTextHeightToWidgets()     -> use AlignTextToFramePadding()\n                       - SetNextWindowPosCenter()            -> use SetNextWindowPos() with a pivot of (0.5f, 0.5f)\n                       - ImFont::Glyph                       -> use ImFontGlyph\n - 2019/10/14 (1.74) - inputs: Fixed a miscalculation in the keyboard/mouse \"typematic\" repeat delay/rate calculation, used by keys and e.g. repeating mouse buttons as well as the GetKeyPressedAmount() function.\n                       if you were using a non-default value for io.KeyRepeatRate (previous default was 0.250), you can add +io.KeyRepeatDelay to it to compensate for the fix.\n                       The function was triggering on: 0.0 and (delay+rate*N) where (N>=1). Fixed formula responds to (N>=0). Effectively it made io.KeyRepeatRate behave like it was set to (io.KeyRepeatRate + io.KeyRepeatDelay).\n                       If you never altered io.KeyRepeatRate nor used GetKeyPressedAmount() this won't affect you.\n - 2019/07/15 (1.72) - removed TreeAdvanceToLabelPos() which is rarely used and only does SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()). Kept redirection function (will obsolete).\n - 2019/07/12 (1.72) - renamed ImFontAtlas::CustomRect to ImFontAtlasCustomRect. Kept redirection typedef (will obsolete).\n - 2019/06/14 (1.72) - removed redirecting functions/enums names that were marked obsolete in 1.51 (June 2017): ImGuiCol_Column*, ImGuiSetCond_*, IsItemHoveredRect(), IsPosHoveringAnyWindow(), IsMouseHoveringAnyWindow(), IsMouseHoveringWindow(), IMGUI_ONCE_UPON_A_FRAME. Grep this log for details and new names, or see how they were implemented until 1.71.\n - 2019/06/07 (1.71) - rendering of child window outer decorations (bg color, border, scrollbars) is now performed as part of the parent window. If you have\n                       overlapping child windows in a same parent, and relied on their relative z-order to be mapped to their submission order, this will affect your rendering.\n                       This optimization is disabled if the parent window has no visual output, because it appears to be the most common situation leading to the creation of overlapping child windows.\n                       Please reach out if you are affected.\n - 2019/05/13 (1.71) - renamed SetNextTreeNodeOpen() to SetNextItemOpen(). Kept inline redirection function (will obsolete).\n - 2019/05/11 (1.71) - changed io.AddInputCharacter(unsigned short c) signature to io.AddInputCharacter(unsigned int c).\n - 2019/04/29 (1.70) - improved ImDrawList thick strokes (>1.0f) preserving correct thickness up to 90 degrees angles (e.g. rectangles). If you have custom rendering using thick lines, they will appear thicker now.\n - 2019/04/29 (1.70) - removed GetContentRegionAvailWidth(), use GetContentRegionAvail().x instead. Kept inline redirection function (will obsolete).\n - 2019/03/04 (1.69) - renamed GetOverlayDrawList() to GetForegroundDrawList(). Kept redirection function (will obsolete).\n - 2019/02/26 (1.69) - renamed ImGuiColorEditFlags_RGB/ImGuiColorEditFlags_HSV/ImGuiColorEditFlags_HEX to ImGuiColorEditFlags_DisplayRGB/ImGuiColorEditFlags_DisplayHSV/ImGuiColorEditFlags_DisplayHex. Kept redirection enums (will obsolete).\n - 2019/02/14 (1.68) - made it illegal/assert when io.DisplayTime == 0.0f (with an exception for the first frame). If for some reason your time step calculation gives you a zero value, replace it with an arbitrarily small value!\n - 2019/02/01 (1.68) - removed io.DisplayVisibleMin/DisplayVisibleMax (which were marked obsolete and removed from viewport/docking branch already).\n - 2019/01/06 (1.67) - renamed io.InputCharacters[], marked internal as was always intended. Please don't access directly, and use AddInputCharacter() instead!\n - 2019/01/06 (1.67) - renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete).\n - 2018/12/20 (1.67) - made it illegal to call Begin(\"\") with an empty string. This somehow half-worked before but had various undesirable side-effects.\n - 2018/12/10 (1.67) - renamed io.ConfigResizeWindowsFromEdges to io.ConfigWindowsResizeFromEdges as we are doing a large pass on configuration flags.\n - 2018/10/12 (1.66) - renamed misc/stl/imgui_stl.* to misc/cpp/imgui_stdlib.* in prevision for other C++ helper files.\n - 2018/09/28 (1.66) - renamed SetScrollHere() to SetScrollHereY(). Kept redirection function (will obsolete).\n - 2018/09/06 (1.65) - renamed stb_truetype.h to imstb_truetype.h, stb_textedit.h to imstb_textedit.h, and stb_rect_pack.h to imstb_rectpack.h.\n                       If you were conveniently using the imgui copy of those STB headers in your project you will have to update your include paths.\n - 2018/09/05 (1.65) - renamed io.OptCursorBlink/io.ConfigCursorBlink to io.ConfigInputTextCursorBlink. (#1427)\n - 2018/08/31 (1.64) - added imgui_widgets.cpp file, extracted and moved widgets code out of imgui.cpp into imgui_widgets.cpp. Re-ordered some of the code remaining in imgui.cpp.\n                       NONE OF THE FUNCTIONS HAVE CHANGED. THE CODE IS SEMANTICALLY 100% IDENTICAL, BUT _EVERY_ FUNCTION HAS BEEN MOVED.\n                       Because of this, any local modifications to imgui.cpp will likely conflict when you update. Read docs/CHANGELOG.txt for suggestions.\n - 2018/08/22 (1.63) - renamed IsItemDeactivatedAfterChange() to IsItemDeactivatedAfterEdit() for consistency with new IsItemEdited() API. Kept redirection function (will obsolete soonish as IsItemDeactivatedAfterChange() is very recent).\n - 2018/08/21 (1.63) - renamed ImGuiTextEditCallback to ImGuiInputTextCallback, ImGuiTextEditCallbackData to ImGuiInputTextCallbackData for consistency. Kept redirection types (will obsolete).\n - 2018/08/21 (1.63) - removed ImGuiInputTextCallbackData::ReadOnly since it is a duplication of (ImGuiInputTextCallbackData::Flags & ImGuiInputTextFlags_ReadOnly).\n - 2018/08/01 (1.63) - removed per-window ImGuiWindowFlags_ResizeFromAnySide beta flag in favor of a global io.ConfigResizeWindowsFromEdges [update 1.67 renamed to ConfigWindowsResizeFromEdges] to enable the feature.\n - 2018/08/01 (1.63) - renamed io.OptCursorBlink to io.ConfigCursorBlink [-> io.ConfigInputTextCursorBlink in 1.65], io.OptMacOSXBehaviors to ConfigMacOSXBehaviors for consistency.\n - 2018/07/22 (1.63) - changed ImGui::GetTime() return value from float to double to avoid accumulating floating point imprecisions over time.\n - 2018/07/08 (1.63) - style: renamed ImGuiCol_ModalWindowDarkening to ImGuiCol_ModalWindowDimBg for consistency with other features. Kept redirection enum (will obsolete).\n - 2018/06/08 (1.62) - examples: the imgui_impl_XXX files have been split to separate platform (Win32, GLFW, SDL2, etc.) from renderer (DX11, OpenGL, Vulkan,  etc.).\n                       old backends will still work as is, however prefer using the separated backends as they will be updated to support multi-viewports.\n                       when adopting new backends follow the main.cpp code of your preferred examples/ folder to know which functions to call.\n                       in particular, note that old backends called ImGui::NewFrame() at the end of their ImGui_ImplXXXX_NewFrame() function.\n - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set.\n - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details.\n - 2018/05/03 (1.61) - DragInt(): the default compile-time format string has been changed from \"%.0f\" to \"%d\", as we are not using integers internally any more.\n                       If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format.\n                       To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code.\n                       If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. \"DragInt.*%f\" to help you find them.\n - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional \"int decimal_precision\" in favor of an equivalent and more flexible \"const char* format\",\n                       consistent with other functions. Kept redirection functions (will obsolete).\n - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value.\n - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some backend ahead of merging the Nav branch).\n - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now.\n - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically.\n - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums.\n - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment.\n - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display.\n - 2018/02/07 (1.60) - reorganized context handling to be more explicit,\n                       - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END.\n                       - removed Shutdown() function, as DestroyContext() serve this purpose.\n                       - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance.\n                       - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts.\n                       - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts.\n - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths.\n - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete).\n - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete).\n - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData.\n - 2017/12/29 (1.60) - removed CalcItemRectClosestPoint() which was weird and not really used by anyone except demo code. If you need it it's easy to replicate on your side.\n - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete).\n - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags\n - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame.\n - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set.\n - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete).\n - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete).\n                     - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete).\n - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete).\n - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete).\n - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed.\n - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up.\n                       Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions.\n - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency.\n - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg.\n - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding.\n - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);\n - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency.\n - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it.\n - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details.\n                       removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting.\n                         IsItemHoveredRect()        --> IsItemHovered(ImGuiHoveredFlags_RectOnly)\n                         IsMouseHoveringAnyWindow() --> IsWindowHovered(ImGuiHoveredFlags_AnyWindow)\n                         IsMouseHoveringWindow()    --> IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) [weird, old behavior]\n - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!\n - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).\n - 2017/09/26 (1.52) - renamed ImFont::Glyph to ImFontGlyph. Kept redirection typedef (will obsolete).\n - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete).\n - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your backend if you need to support unavailable mouse, make sure to replace \"io.MousePos = ImVec2(-1,-1)\" with \"io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)\".\n - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!\n                     - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).\n                     - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).\n - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.\n - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an \"ambiguous call\" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix.\n - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame type.\n - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely.\n - 2017/08/13 (1.51) - renamed ImGuiCol_Column to ImGuiCol_Separator, ImGuiCol_ColumnHovered to ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive to ImGuiCol_SeparatorActive. Kept redirection enums (will obsolete).\n - 2017/08/11 (1.51) - renamed ImGuiSetCond_Always to ImGuiCond_Always, ImGuiSetCond_Once to ImGuiCond_Once, ImGuiSetCond_FirstUseEver to ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing to ImGuiCond_Appearing. Kept redirection enums (will obsolete).\n - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton().\n - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu.\n                     - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under \"Color/Picker Widgets\", to understand the various new options.\n                     - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0, 0))'\n - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse\n - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset.\n - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity.\n - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetID() and use it instead of passing string to BeginChild().\n - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it.\n - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc.\n - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully, breakage should be minimal.\n - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore.\n                       If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you, otherwise if <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar.\n                       This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color:\n                       ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) { float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); }\n                       If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color.\n - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext().\n - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection.\n - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the \"default_open = true\" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen).\n - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDrawList::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer.\n - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref GitHub issue #337).\n - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337)\n - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete).\n - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert.\n - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you.\n - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis.\n - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete.\n - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position.\n                       GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side.\n                       GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out!\n - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize\n - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project.\n - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason\n - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure.\n                       you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text.\n - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost.\n                       this necessary change will break your rendering function! the fix should be very easy. sorry for that :(\n                     - if you are using a vanilla copy of one of the imgui_impl_XXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.\n                     - the signature of the io.RenderDrawListsFn handler has changed!\n                       old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count)\n                       new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data).\n                         parameters: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount'\n                         ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new.\n                         ImDrawCmd:  'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'.\n                     - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer.\n                     - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering!\n                     - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade!\n - 2015/07/10 (1.43) - changed SameLine() parameters from int to float.\n - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete).\n - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount.\n - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence\n - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely used. Sorry!\n - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete).\n - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete).\n - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons.\n - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the \"open\" state of a popup. BeginPopup() returns true if the popup is opened.\n - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same).\n - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50.\n - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API\n - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive.\n - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead.\n - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50.\n - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing\n - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50.\n - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing)\n - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50.\n - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once.\n - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now.\n - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior\n - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing()\n - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused)\n - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions.\n - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader.\n - 2015/01/11 (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels.\n                       - old:  const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); [..Upload texture to GPU..];\n                       - new:  unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); [..Upload texture to GPU..]; io.Fonts->SetTexID(YourTexIdentifier);\n                       you now have more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. It is now recommended that you sample the font texture with bilinear interpolation.\n - 2015/01/11 (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to call io.Fonts->SetTexID()\n - 2015/01/11 (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix)\n - 2015/01/11 (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets\n - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver)\n - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph)\n - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility\n - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered()\n - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly)\n - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity)\n - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale()\n - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn\n - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically)\n - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite\n - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes\n\n\n FREQUENTLY ASKED QUESTIONS (FAQ)\n ================================\n\n Read all answers online:\n   https://www.dearimgui.com/faq or https://github.com/ocornut/imgui/blob/master/docs/FAQ.md (same url)\n Read all answers locally (with a text editor or ideally a Markdown viewer):\n   docs/FAQ.md\n Some answers are copied down here to facilitate searching in code.\n\n Q&A: Basics\n ===========\n\n Q: Where is the documentation?\n A: This library is poorly documented at the moment and expects the user to be acquainted with C/C++.\n    - Run the examples/ applications and explore them.\n    - Read Getting Started (https://github.com/ocornut/imgui/wiki/Getting-Started) guide.\n    - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function.\n    - The demo covers most features of Dear ImGui, so you can read the code and see its output.\n    - See documentation and comments at the top of imgui.cpp + effectively imgui.h.\n    - 20+ standalone example applications using e.g. OpenGL/DirectX are provided in the\n      examples/ folder to explain how to integrate Dear ImGui with your own engine/application.\n    - The Wiki (https://github.com/ocornut/imgui/wiki) has many resources and links.\n    - The Glossary (https://github.com/ocornut/imgui/wiki/Glossary) page also may be useful.\n    - Your programming IDE is your friend, find the type or function declaration to find comments\n      associated with it.\n\n Q: What is this library called?\n Q: Which version should I get?\n >> This library is called \"Dear ImGui\", please don't call it \"ImGui\" :)\n >> See https://www.dearimgui.com/faq for details.\n\n Q&A: Integration\n ================\n\n Q: How to get started?\n A: Read https://github.com/ocornut/imgui/wiki/Getting-Started. Read 'PROGRAMMER GUIDE' above. Read examples/README.txt.\n\n Q: How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?\n A: You should read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags!\n >> See https://www.dearimgui.com/faq for a fully detailed answer. You really want to read this.\n\n Q. How can I enable keyboard or gamepad controls?\n Q: How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)\n Q: I integrated Dear ImGui in my engine and little squares are showing instead of text...\n Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...\n Q: I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...\n >> See https://www.dearimgui.com/faq\n\n Q&A: Usage\n ----------\n\n Q: About the ID Stack system..\n   - Why is my widget not reacting when I click on it?\n   - How can I have widgets with an empty label?\n   - How can I have multiple widgets with the same label?\n   - How can I have multiple windows with the same label?\n Q: How can I display an image? What is ImTextureID, how does it work?\n Q: How can I use my own math types instead of ImVec2?\n Q: How can I interact with standard C++ types (such as std::string and std::vector)?\n Q: How can I display custom shapes? (using low-level ImDrawList API)\n >> See https://www.dearimgui.com/faq\n\n Q&A: Fonts, Text\n ================\n\n Q: How should I handle DPI in my application?\n Q: How can I load a different font than the default?\n Q: How can I easily use icons in my application?\n Q: How can I load multiple fonts?\n Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic?\n >> See https://www.dearimgui.com/faq and https://github.com/ocornut/imgui/edit/master/docs/FONTS.md\n\n Q&A: Concerns\n =============\n\n Q: Who uses Dear ImGui?\n Q: Can you create elaborate/serious tools with Dear ImGui?\n Q: Can you reskin the look of Dear ImGui?\n Q: Why using C++ (as opposed to C)?\n >> See https://www.dearimgui.com/faq\n\n Q&A: Community\n ==============\n\n Q: How can I help?\n A: - Businesses: please reach out to \"omar AT dearimgui DOT com\" if you work in a place using Dear ImGui!\n      We can discuss ways for your company to fund development via invoiced technical support, maintenance or sponsoring contacts.\n      This is among the most useful thing you can do for Dear ImGui. With increased funding, we sustain and grow work on this project.\n      Also see https://github.com/ocornut/imgui/wiki/Sponsors\n    - Businesses: you can also purchase licenses for the Dear ImGui Automation/Test Engine.\n    - If you are experienced with Dear ImGui and C++, look at the GitHub issues, look at the Wiki, and see how you want to help and can help!\n    - Disclose your usage of Dear ImGui via a dev blog post, a tweet, a screenshot, a mention somewhere etc.\n      You may post screenshot or links in the gallery threads. Visuals are ideal as they inspire other programmers.\n      But even without visuals, disclosing your use of dear imgui helps the library grow credibility, and help other teams and programmers with taking decisions.\n    - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on GitHub or privately).\n\n*/\n\n//-------------------------------------------------------------------------\n// [SECTION] INCLUDES\n//-------------------------------------------------------------------------\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#ifndef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS\n#endif\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_internal.h\"\n\n// System includes\n#include <stdio.h>      // vsnprintf, sscanf, printf\n#include <stdint.h>     // intptr_t\n\n// [Windows] On non-Visual Studio compilers, we default to IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS unless explicitly enabled\n#if defined(_WIN32) && !defined(_MSC_VER) && !defined(IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)\n#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS\n#endif\n\n// [Windows] OS specific includes (optional)\n#if defined(_WIN32) && defined(IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) && defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)\n#define IMGUI_DISABLE_WIN32_FUNCTIONS\n#endif\n#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS)\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n#ifndef NOMINMAX\n#define NOMINMAX\n#endif\n#ifndef __MINGW32__\n#include <Windows.h>        // _wfopen, OpenClipboard\n#else\n#include <windows.h>\n#endif\n#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) // UWP doesn't have all Win32 functions\n#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS\n#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS\n#endif\n#endif\n\n// [Apple] OS specific includes\n#if defined(__APPLE__)\n#include <TargetConditionals.h>\n#endif\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4127)             // condition expression is constant\n#pragma warning (disable: 4996)             // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#if defined(_MSC_VER) && _MSC_VER >= 1922   // MSVC 2019 16.2 or later\n#pragma warning (disable: 5054)             // operator '|': deprecated between enumerations of different types\n#endif\n#pragma warning (disable: 26451)            // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).\n#pragma warning (disable: 26495)            // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).\n#pragma warning (disable: 26812)            // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast                            // yes, they are more terse.\n#pragma clang diagnostic ignored \"-Wfloat-equal\"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.\n#pragma clang diagnostic ignored \"-Wformat-nonliteral\"              // warning: format string is not a string literal            // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.\n#pragma clang diagnostic ignored \"-Wexit-time-destructors\"          // warning: declaration requires an exit-time destructor     // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.\n#pragma clang diagnostic ignored \"-Wglobal-constructors\"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.\n#pragma clang diagnostic ignored \"-Wsign-conversion\"                // warning: implicit conversion changes signedness\n#pragma clang diagnostic ignored \"-Wformat-pedantic\"                // warning: format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic.\n#pragma clang diagnostic ignored \"-Wint-to-void-pointer-cast\"       // warning: cast to 'void *' from smaller integer type 'int'\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#elif defined(__GNUC__)\n// We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association.\n#pragma GCC diagnostic ignored \"-Wpragmas\"                  // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wunused-function\"          // warning: 'xxxx' defined but not used\n#pragma GCC diagnostic ignored \"-Wint-to-pointer-cast\"      // warning: cast to pointer from integer of different size\n#pragma GCC diagnostic ignored \"-Wformat\"                   // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*'\n#pragma GCC diagnostic ignored \"-Wdouble-promotion\"         // warning: implicit conversion from 'float' to 'double' when passing argument to function\n#pragma GCC diagnostic ignored \"-Wconversion\"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value\n#pragma GCC diagnostic ignored \"-Wformat-nonliteral\"        // warning: format not a string literal, format string not checked\n#pragma GCC diagnostic ignored \"-Wstrict-overflow\"          // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"          // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#endif\n\n// Debug options\n#define IMGUI_DEBUG_NAV_SCORING     0   // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL\n#define IMGUI_DEBUG_NAV_RECTS       0   // Display the reference navigation rectangle for each window\n\n// When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch.\nstatic const float NAV_WINDOWING_HIGHLIGHT_DELAY            = 0.20f;    // Time before the highlight and screen dimming starts fading in\nstatic const float NAV_WINDOWING_LIST_APPEAR_DELAY          = 0.15f;    // Time before the window list starts to appear\n\n// Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend)\nstatic const float WINDOWS_HOVER_PADDING                    = 4.0f;     // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow().\nstatic const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f;    // Reduce visual noise by only highlighting the border after a certain time.\nstatic const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER    = 0.70f;    // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.\n\n// Tooltip offset\nstatic const ImVec2 TOOLTIP_DEFAULT_OFFSET = ImVec2(16, 10);            // Multiplied by g.Style.MouseCursorScale\n\n//-------------------------------------------------------------------------\n// [SECTION] FORWARD DECLARATIONS\n//-------------------------------------------------------------------------\n\nstatic void             SetCurrentWindow(ImGuiWindow* window);\nstatic void             FindHoveredWindow();\nstatic ImGuiWindow*     CreateNewWindow(const char* name, ImGuiWindowFlags flags);\nstatic ImVec2           CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window);\n\nstatic void             AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window);\n\n// Settings\nstatic void             WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*);\nstatic void*            WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name);\nstatic void             WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line);\nstatic void             WindowSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*);\nstatic void             WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSettingsHandler*, ImGuiTextBuffer* buf);\n\n// Platform Dependents default implementation for IO functions\nstatic const char*      GetClipboardTextFn_DefaultImpl(void* user_data_ctx);\nstatic void             SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text);\nstatic void             SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data);\n\nnamespace ImGui\n{\n// Navigation\nstatic void             NavUpdate();\nstatic void             NavUpdateWindowing();\nstatic void             NavUpdateWindowingOverlay();\nstatic void             NavUpdateCancelRequest();\nstatic void             NavUpdateCreateMoveRequest();\nstatic void             NavUpdateCreateTabbingRequest();\nstatic float            NavUpdatePageUpPageDown();\nstatic inline void      NavUpdateAnyRequestFlag();\nstatic void             NavUpdateCreateWrappingRequest();\nstatic void             NavEndFrame();\nstatic bool             NavScoreItem(ImGuiNavItemData* result);\nstatic void             NavApplyItemToResult(ImGuiNavItemData* result);\nstatic void             NavProcessItem();\nstatic void             NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags);\nstatic ImVec2           NavCalcPreferredRefPos();\nstatic void             NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window);\nstatic ImGuiWindow*     NavRestoreLastChildNavWindow(ImGuiWindow* window);\nstatic void             NavRestoreLayer(ImGuiNavLayer layer);\nstatic int              FindWindowFocusIndex(ImGuiWindow* window);\n\n// Error Checking and Debug Tools\nstatic void             ErrorCheckNewFrameSanityChecks();\nstatic void             ErrorCheckEndFrameSanityChecks();\nstatic void             UpdateDebugToolItemPicker();\nstatic void             UpdateDebugToolStackQueries();\n\n// Inputs\nstatic void             UpdateKeyboardInputs();\nstatic void             UpdateMouseInputs();\nstatic void             UpdateMouseWheel();\nstatic void             UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt);\n\n// Misc\nstatic void             UpdateSettings();\nstatic int              UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect);\nstatic void             RenderWindowOuterBorders(ImGuiWindow* window);\nstatic void             RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size);\nstatic void             RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open);\nstatic void             RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col);\nstatic void             RenderDimmedBackgrounds();\n\n// Viewports\nstatic void             UpdateViewportsNewFrame();\n\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] CONTEXT AND MEMORY ALLOCATORS\n//-----------------------------------------------------------------------------\n\n// DLL users:\n// - Heaps and globals are not shared across DLL boundaries!\n// - You will need to call SetCurrentContext() + SetAllocatorFunctions() for each static/DLL boundary you are calling from.\n// - Same applies for hot-reloading mechanisms that are reliant on reloading DLL (note that many hot-reloading mechanisms work without DLL).\n// - Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.\n// - Confused? In a debugger: add GImGui to your watch window and notice how its value changes depending on your current location (which DLL boundary you are in).\n\n// Current context pointer. Implicitly used by all Dear ImGui functions. Always assumed to be != NULL.\n// - ImGui::CreateContext() will automatically set this pointer if it is NULL.\n//   Change to a different context by calling ImGui::SetCurrentContext().\n// - Important: Dear ImGui functions are not thread-safe because of this pointer.\n//   If you want thread-safety to allow N threads to access N different contexts:\n//   - Change this variable to use thread local storage so each thread can refer to a different context, in your imconfig.h:\n//         struct ImGuiContext;\n//         extern thread_local ImGuiContext* MyImGuiTLS;\n//         #define GImGui MyImGuiTLS\n//     And then define MyImGuiTLS in one of your cpp files. Note that thread_local is a C++11 keyword, earlier C++ uses compiler-specific keyword.\n//   - Future development aims to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586\n//   - If you need a finite number of contexts, you may compile and use multiple instances of the ImGui code from a different namespace.\n// - DLL users: read comments above.\n#ifndef GImGui\nImGuiContext*   GImGui = NULL;\n#endif\n\n// Memory Allocator functions. Use SetAllocatorFunctions() to change them.\n// - You probably don't want to modify that mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction.\n// - DLL users: read comments above.\n#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS\nstatic void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); return malloc(size); }\nstatic void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); free(ptr); }\n#else\nstatic void*   MallocWrapper(size_t size, void* user_data)    { IM_UNUSED(user_data); IM_UNUSED(size); IM_ASSERT(0); return NULL; }\nstatic void    FreeWrapper(void* ptr, void* user_data)        { IM_UNUSED(user_data); IM_UNUSED(ptr); IM_ASSERT(0); }\n#endif\nstatic ImGuiMemAllocFunc    GImAllocatorAllocFunc = MallocWrapper;\nstatic ImGuiMemFreeFunc     GImAllocatorFreeFunc = FreeWrapper;\nstatic void*                GImAllocatorUserData = NULL;\n\n//-----------------------------------------------------------------------------\n// [SECTION] USER FACING STRUCTURES (ImGuiStyle, ImGuiIO)\n//-----------------------------------------------------------------------------\n\nImGuiStyle::ImGuiStyle()\n{\n    Alpha                   = 1.0f;             // Global alpha applies to everything in Dear ImGui.\n    DisabledAlpha           = 0.60f;            // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.\n    WindowPadding           = ImVec2(8,8);      // Padding within a window\n    WindowRounding          = 0.0f;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.\n    WindowBorderSize        = 1.0f;             // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested.\n    WindowMinSize           = ImVec2(32,32);    // Minimum window size\n    WindowTitleAlign        = ImVec2(0.0f,0.5f);// Alignment for title bar text\n    WindowMenuButtonPosition= ImGuiDir_Left;    // Position of the collapsing/docking button in the title bar (left/right). Defaults to ImGuiDir_Left.\n    ChildRounding           = 0.0f;             // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows\n    ChildBorderSize         = 1.0f;             // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested.\n    PopupRounding           = 0.0f;             // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows\n    PopupBorderSize         = 1.0f;             // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested.\n    FramePadding            = ImVec2(4,3);      // Padding within a framed rectangle (used by most widgets)\n    FrameRounding           = 0.0f;             // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets).\n    FrameBorderSize         = 0.0f;             // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested.\n    ItemSpacing             = ImVec2(8,4);      // Horizontal and vertical spacing between widgets/lines\n    ItemInnerSpacing        = ImVec2(4,4);      // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label)\n    CellPadding             = ImVec2(4,2);      // Padding within a table cell. CellPadding.y may be altered between different rows.\n    TouchExtraPadding       = ImVec2(0,0);      // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!\n    IndentSpacing           = 21.0f;            // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).\n    ColumnsMinSpacing       = 6.0f;             // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).\n    ScrollbarSize           = 14.0f;            // Width of the vertical scrollbar, Height of the horizontal scrollbar\n    ScrollbarRounding       = 9.0f;             // Radius of grab corners rounding for scrollbar\n    GrabMinSize             = 12.0f;            // Minimum width/height of a grab box for slider/scrollbar\n    GrabRounding            = 0.0f;             // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.\n    LogSliderDeadzone       = 4.0f;             // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.\n    TabRounding             = 4.0f;             // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.\n    TabBorderSize           = 0.0f;             // Thickness of border around tabs.\n    TabMinWidthForCloseButton = 0.0f;           // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.\n    TabBarBorderSize        = 1.0f;             // Thickness of tab-bar separator, which takes on the tab active color to denote focus.\n    TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees).\n    ColorButtonPosition     = ImGuiDir_Right;   // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.\n    ButtonTextAlign         = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text.\n    SelectableTextAlign     = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.\n    SeparatorTextBorderSize = 3.0f;             // Thickkness of border in SeparatorText()\n    SeparatorTextAlign      = ImVec2(0.0f,0.5f);// Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).\n    SeparatorTextPadding    = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.\n    DisplayWindowPadding    = ImVec2(19,19);    // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.\n    DisplaySafeAreaPadding  = ImVec2(3,3);      // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows.\n    MouseCursorScale        = 1.0f;             // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.\n    AntiAliasedLines        = true;             // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU.\n    AntiAliasedLinesUseTex  = true;             // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).\n    AntiAliasedFill         = true;             // Enable anti-aliased filled shapes (rounded rectangles, circles, etc.).\n    CurveTessellationTol    = 1.25f;            // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.\n    CircleTessellationMaxError = 0.30f;         // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.\n\n    // Behaviors\n    HoverStationaryDelay    = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.\n    HoverDelayShort         = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.\n    HoverDelayNormal        = 0.40f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). \"\n    HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_AllowWhenDisabled;    // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.\n    HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_AllowWhenDisabled;  // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.\n\n    // Default theme\n    ImGui::StyleColorsDark(this);\n}\n\n// To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you.\n// Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times.\nvoid ImGuiStyle::ScaleAllSizes(float scale_factor)\n{\n    WindowPadding = ImTrunc(WindowPadding * scale_factor);\n    WindowRounding = ImTrunc(WindowRounding * scale_factor);\n    WindowMinSize = ImTrunc(WindowMinSize * scale_factor);\n    ChildRounding = ImTrunc(ChildRounding * scale_factor);\n    PopupRounding = ImTrunc(PopupRounding * scale_factor);\n    FramePadding = ImTrunc(FramePadding * scale_factor);\n    FrameRounding = ImTrunc(FrameRounding * scale_factor);\n    ItemSpacing = ImTrunc(ItemSpacing * scale_factor);\n    ItemInnerSpacing = ImTrunc(ItemInnerSpacing * scale_factor);\n    CellPadding = ImTrunc(CellPadding * scale_factor);\n    TouchExtraPadding = ImTrunc(TouchExtraPadding * scale_factor);\n    IndentSpacing = ImTrunc(IndentSpacing * scale_factor);\n    ColumnsMinSpacing = ImTrunc(ColumnsMinSpacing * scale_factor);\n    ScrollbarSize = ImTrunc(ScrollbarSize * scale_factor);\n    ScrollbarRounding = ImTrunc(ScrollbarRounding * scale_factor);\n    GrabMinSize = ImTrunc(GrabMinSize * scale_factor);\n    GrabRounding = ImTrunc(GrabRounding * scale_factor);\n    LogSliderDeadzone = ImTrunc(LogSliderDeadzone * scale_factor);\n    TabRounding = ImTrunc(TabRounding * scale_factor);\n    TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImTrunc(TabMinWidthForCloseButton * scale_factor) : FLT_MAX;\n    SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor);\n    DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor);\n    DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor);\n    MouseCursorScale = ImTrunc(MouseCursorScale * scale_factor);\n}\n\nImGuiIO::ImGuiIO()\n{\n    // Most fields are initialized with zero\n    memset(this, 0, sizeof(*this));\n    IM_STATIC_ASSERT(IM_ARRAYSIZE(ImGuiIO::MouseDown) == ImGuiMouseButton_COUNT && IM_ARRAYSIZE(ImGuiIO::MouseClicked) == ImGuiMouseButton_COUNT);\n\n    // Settings\n    ConfigFlags = ImGuiConfigFlags_None;\n    BackendFlags = ImGuiBackendFlags_None;\n    DisplaySize = ImVec2(-1.0f, -1.0f);\n    DeltaTime = 1.0f / 60.0f;\n    IniSavingRate = 5.0f;\n    IniFilename = \"imgui.ini\"; // Important: \"imgui.ini\" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables).\n    LogFilename = \"imgui_log.txt\";\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    for (int i = 0; i < ImGuiKey_COUNT; i++)\n        KeyMap[i] = -1;\n#endif\n    UserData = NULL;\n\n    Fonts = NULL;\n    FontGlobalScale = 1.0f;\n    FontDefault = NULL;\n    FontAllowUserScaling = false;\n    DisplayFramebufferScale = ImVec2(1.0f, 1.0f);\n\n    MouseDoubleClickTime = 0.30f;\n    MouseDoubleClickMaxDist = 6.0f;\n    MouseDragThreshold = 6.0f;\n    KeyRepeatDelay = 0.275f;\n    KeyRepeatRate = 0.050f;\n\n    // Miscellaneous options\n    MouseDrawCursor = false;\n#ifdef __APPLE__\n    ConfigMacOSXBehaviors = true;  // Set Mac OS X style defaults based on __APPLE__ compile time flag\n#else\n    ConfigMacOSXBehaviors = false;\n#endif\n    ConfigInputTrickleEventQueue = true;\n    ConfigInputTextCursorBlink = true;\n    ConfigInputTextEnterKeepActive = false;\n    ConfigDragClickToInputText = false;\n    ConfigWindowsResizeFromEdges = true;\n    ConfigWindowsMoveFromTitleBarOnly = false;\n    ConfigMemoryCompactTimer = 60.0f;\n    ConfigDebugBeginReturnValueOnce = false;\n    ConfigDebugBeginReturnValueLoop = false;\n\n    // Platform Functions\n    // Note: Initialize() will setup default clipboard/ime handlers.\n    BackendPlatformName = BackendRendererName = NULL;\n    BackendPlatformUserData = BackendRendererUserData = BackendLanguageUserData = NULL;\n    PlatformLocaleDecimalPoint = '.';\n\n    // Input (NB: we already have memset zero the entire structure!)\n    MousePos = ImVec2(-FLT_MAX, -FLT_MAX);\n    MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);\n    MouseSource = ImGuiMouseSource_Mouse;\n    for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;\n    for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }\n    AppAcceptingEvents = true;\n    BackendUsingLegacyKeyArrays = (ImS8)-1;\n    BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong\n}\n\n// Pass in translated ASCII characters for text input.\n// - with glfw you can get those from the callback set in glfwSetCharCallback()\n// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message\n// FIXME: Should in theory be called \"AddCharacterEvent()\" to be consistent with new API\nvoid ImGuiIO::AddInputCharacter(unsigned int c)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n    if (c == 0 || !AppAcceptingEvents)\n        return;\n\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_Text;\n    e.Source = ImGuiInputSource_Keyboard;\n    e.EventId = g.InputEventsNextEventId++;\n    e.Text.Char = c;\n    g.InputEventsQueue.push_back(e);\n}\n\n// UTF16 strings use surrogate pairs to encode codepoints >= 0x10000, so\n// we should save the high surrogate.\nvoid ImGuiIO::AddInputCharacterUTF16(ImWchar16 c)\n{\n    if ((c == 0 && InputQueueSurrogate == 0) || !AppAcceptingEvents)\n        return;\n\n    if ((c & 0xFC00) == 0xD800) // High surrogate, must save\n    {\n        if (InputQueueSurrogate != 0)\n            AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);\n        InputQueueSurrogate = c;\n        return;\n    }\n\n    ImWchar cp = c;\n    if (InputQueueSurrogate != 0)\n    {\n        if ((c & 0xFC00) != 0xDC00) // Invalid low surrogate\n        {\n            AddInputCharacter(IM_UNICODE_CODEPOINT_INVALID);\n        }\n        else\n        {\n#if IM_UNICODE_CODEPOINT_MAX == 0xFFFF\n            cp = IM_UNICODE_CODEPOINT_INVALID; // Codepoint will not fit in ImWchar\n#else\n            cp = (ImWchar)(((InputQueueSurrogate - 0xD800) << 10) + (c - 0xDC00) + 0x10000);\n#endif\n        }\n\n        InputQueueSurrogate = 0;\n    }\n    AddInputCharacter((unsigned)cp);\n}\n\nvoid ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars)\n{\n    if (!AppAcceptingEvents)\n        return;\n    while (*utf8_chars != 0)\n    {\n        unsigned int c = 0;\n        utf8_chars += ImTextCharFromUtf8(&c, utf8_chars, NULL);\n        AddInputCharacter(c);\n    }\n}\n\n// Clear all incoming events.\nvoid ImGuiIO::ClearEventsQueue()\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n    g.InputEventsQueue.clear();\n}\n\n// Clear current keyboard/mouse/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.\nvoid ImGuiIO::ClearInputKeys()\n{\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    memset(KeysDown, 0, sizeof(KeysDown));\n#endif\n    for (int n = 0; n < IM_ARRAYSIZE(KeysData); n++)\n    {\n        KeysData[n].Down             = false;\n        KeysData[n].DownDuration     = -1.0f;\n        KeysData[n].DownDurationPrev = -1.0f;\n    }\n    KeyCtrl = KeyShift = KeyAlt = KeySuper = false;\n    KeyMods = ImGuiMod_None;\n    MousePos = ImVec2(-FLT_MAX, -FLT_MAX);\n    for (int n = 0; n < IM_ARRAYSIZE(MouseDown); n++)\n    {\n        MouseDown[n] = false;\n        MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f;\n    }\n    MouseWheel = MouseWheelH = 0.0f;\n    InputQueueCharacters.resize(0); // Behavior of old ClearInputCharacters().\n}\n\n// Removed this as it is ambiguous/misleading and generally incorrect to use with the existence of a higher-level input queue.\n// Current frame character buffer is now also cleared by ClearInputKeys().\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\nvoid ImGuiIO::ClearInputCharacters()\n{\n    InputQueueCharacters.resize(0);\n}\n#endif\n\nstatic ImGuiInputEvent* FindLatestInputEvent(ImGuiContext* ctx, ImGuiInputEventType type, int arg = -1)\n{\n    ImGuiContext& g = *ctx;\n    for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--)\n    {\n        ImGuiInputEvent* e = &g.InputEventsQueue[n];\n        if (e->Type != type)\n            continue;\n        if (type == ImGuiInputEventType_Key && e->Key.Key != arg)\n            continue;\n        if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg)\n            continue;\n        return e;\n    }\n    return NULL;\n}\n\n// Queue a new key down/up event.\n// - ImGuiKey key:       Translated key (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)\n// - bool down:          Is the key down? use false to signify a key release.\n// - float analog_value: 0.0f..1.0f\n// IMPORTANT: THIS FUNCTION AND OTHER \"ADD\" GRABS THE CONTEXT FROM OUR INSTANCE.\n// WE NEED TO ENSURE THAT ALL FUNCTION CALLS ARE FULLFILLING THIS, WHICH IS WHY GetKeyData() HAS AN EXPLICIT CONTEXT.\nvoid ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value)\n{\n    //if (e->Down) { IMGUI_DEBUG_LOG_IO(\"AddKeyEvent() Key='%s' %d, NativeKeycode = %d, NativeScancode = %d\\n\", ImGui::GetKeyName(e->Key), e->Down, e->NativeKeycode, e->NativeScancode); }\n    IM_ASSERT(Ctx != NULL);\n    if (key == ImGuiKey_None || !AppAcceptingEvents)\n        return;\n    ImGuiContext& g = *Ctx;\n    IM_ASSERT(ImGui::IsNamedKeyOrModKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API.\n    IM_ASSERT(ImGui::IsAliasKey(key) == false); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events.\n    IM_ASSERT(key != ImGuiMod_Shortcut); // We could easily support the translation here but it seems saner to not accept it (TestEngine perform a translation itself)\n\n    // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data.\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    IM_ASSERT((BackendUsingLegacyKeyArrays == -1 || BackendUsingLegacyKeyArrays == 0) && \"Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!\");\n    if (BackendUsingLegacyKeyArrays == -1)\n        for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)\n            IM_ASSERT(KeyMap[n] == -1 && \"Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!\");\n    BackendUsingLegacyKeyArrays = 0;\n#endif\n    if (ImGui::IsGamepadKey(key))\n        BackendUsingLegacyNavInputArray = false;\n\n    // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed)\n    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Key, (int)key);\n    const ImGuiKeyData* key_data = ImGui::GetKeyData(&g, key);\n    const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down;\n    const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue;\n    if (latest_key_down == down && latest_key_analog == analog_value)\n        return;\n\n    // Add event\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_Key;\n    e.Source = ImGui::IsGamepadKey(key) ? ImGuiInputSource_Gamepad : ImGuiInputSource_Keyboard;\n    e.EventId = g.InputEventsNextEventId++;\n    e.Key.Key = key;\n    e.Key.Down = down;\n    e.Key.AnalogValue = analog_value;\n    g.InputEventsQueue.push_back(e);\n}\n\nvoid ImGuiIO::AddKeyEvent(ImGuiKey key, bool down)\n{\n    if (!AppAcceptingEvents)\n        return;\n    AddKeyAnalogEvent(key, down, down ? 1.0f : 0.0f);\n}\n\n// [Optional] Call after AddKeyEvent().\n// Specify native keycode, scancode + Specify index for legacy <1.87 IsKeyXXX() functions with native indices.\n// If you are writing a backend in 2022 or don't use IsKeyXXX() with native values that are not ImGuiKey values, you can avoid calling this.\nvoid ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index)\n{\n    if (key == ImGuiKey_None)\n        return;\n    IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512\n    IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511\n    IM_UNUSED(native_keycode);  // Yet unused\n    IM_UNUSED(native_scancode); // Yet unused\n\n    // Build native->imgui map so old user code can still call key functions with native 0..511 values.\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    const int legacy_key = (native_legacy_index != -1) ? native_legacy_index : native_keycode;\n    if (!ImGui::IsLegacyKey((ImGuiKey)legacy_key))\n        return;\n    KeyMap[legacy_key] = key;\n    KeyMap[key] = legacy_key;\n#else\n    IM_UNUSED(key);\n    IM_UNUSED(native_legacy_index);\n#endif\n}\n\n// Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.\nvoid ImGuiIO::SetAppAcceptingEvents(bool accepting_events)\n{\n    AppAcceptingEvents = accepting_events;\n}\n\n// Queue a mouse move event\nvoid ImGuiIO::AddMousePosEvent(float x, float y)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n    if (!AppAcceptingEvents)\n        return;\n\n    // Apply same flooring as UpdateMouseInputs()\n    ImVec2 pos((x > -FLT_MAX) ? ImFloor(x) : x, (y > -FLT_MAX) ? ImFloor(y) : y);\n\n    // Filter duplicate\n    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MousePos);\n    const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos;\n    if (latest_pos.x == pos.x && latest_pos.y == pos.y)\n        return;\n\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_MousePos;\n    e.Source = ImGuiInputSource_Mouse;\n    e.EventId = g.InputEventsNextEventId++;\n    e.MousePos.PosX = pos.x;\n    e.MousePos.PosY = pos.y;\n    e.MousePos.MouseSource = g.InputEventsNextMouseSource;\n    g.InputEventsQueue.push_back(e);\n}\n\nvoid ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n    IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT);\n    if (!AppAcceptingEvents)\n        return;\n\n    // Filter duplicate\n    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseButton, (int)mouse_button);\n    const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button];\n    if (latest_button_down == down)\n        return;\n\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_MouseButton;\n    e.Source = ImGuiInputSource_Mouse;\n    e.EventId = g.InputEventsNextEventId++;\n    e.MouseButton.Button = mouse_button;\n    e.MouseButton.Down = down;\n    e.MouseButton.MouseSource = g.InputEventsNextMouseSource;\n    g.InputEventsQueue.push_back(e);\n}\n\n// Queue a mouse wheel event (some mouse/API may only have a Y component)\nvoid ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n\n    // Filter duplicate (unlike most events, wheel values are relative and easy to filter)\n    if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f))\n        return;\n\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_MouseWheel;\n    e.Source = ImGuiInputSource_Mouse;\n    e.EventId = g.InputEventsNextEventId++;\n    e.MouseWheel.WheelX = wheel_x;\n    e.MouseWheel.WheelY = wheel_y;\n    e.MouseWheel.MouseSource = g.InputEventsNextMouseSource;\n    g.InputEventsQueue.push_back(e);\n}\n\n// This is not a real event, the data is latched in order to be stored in actual Mouse events.\n// This is so that duplicate events (e.g. Windows sending extraneous WM_MOUSEMOVE) gets filtered and are not leading to actual source changes.\nvoid ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n    g.InputEventsNextMouseSource = source;\n}\n\nvoid ImGuiIO::AddFocusEvent(bool focused)\n{\n    IM_ASSERT(Ctx != NULL);\n    ImGuiContext& g = *Ctx;\n\n    // Filter duplicate\n    const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_Focus);\n    const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost;\n    if (latest_focused == focused || (ConfigDebugIgnoreFocusLoss && !focused))\n        return;\n\n    ImGuiInputEvent e;\n    e.Type = ImGuiInputEventType_Focus;\n    e.EventId = g.InputEventsNextEventId++;\n    e.AppFocused.Focused = focused;\n    g.InputEventsQueue.push_back(e);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] MISC HELPERS/UTILITIES (Geometry functions)\n//-----------------------------------------------------------------------------\n\nImVec2 ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments)\n{\n    IM_ASSERT(num_segments > 0); // Use ImBezierCubicClosestPointCasteljau()\n    ImVec2 p_last = p1;\n    ImVec2 p_closest;\n    float p_closest_dist2 = FLT_MAX;\n    float t_step = 1.0f / (float)num_segments;\n    for (int i_step = 1; i_step <= num_segments; i_step++)\n    {\n        ImVec2 p_current = ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step);\n        ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);\n        float dist2 = ImLengthSqr(p - p_line);\n        if (dist2 < p_closest_dist2)\n        {\n            p_closest = p_line;\n            p_closest_dist2 = dist2;\n        }\n        p_last = p_current;\n    }\n    return p_closest;\n}\n\n// Closely mimics PathBezierToCasteljau() in imgui_draw.cpp\nstatic void ImBezierCubicClosestPointCasteljauStep(const ImVec2& p, ImVec2& p_closest, ImVec2& p_last, float& p_closest_dist2, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)\n{\n    float dx = x4 - x1;\n    float dy = y4 - y1;\n    float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);\n    float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);\n    d2 = (d2 >= 0) ? d2 : -d2;\n    d3 = (d3 >= 0) ? d3 : -d3;\n    if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))\n    {\n        ImVec2 p_current(x4, y4);\n        ImVec2 p_line = ImLineClosestPoint(p_last, p_current, p);\n        float dist2 = ImLengthSqr(p - p_line);\n        if (dist2 < p_closest_dist2)\n        {\n            p_closest = p_line;\n            p_closest_dist2 = dist2;\n        }\n        p_last = p_current;\n    }\n    else if (level < 10)\n    {\n        float x12 = (x1 + x2)*0.5f,       y12 = (y1 + y2)*0.5f;\n        float x23 = (x2 + x3)*0.5f,       y23 = (y2 + y3)*0.5f;\n        float x34 = (x3 + x4)*0.5f,       y34 = (y3 + y4)*0.5f;\n        float x123 = (x12 + x23)*0.5f,    y123 = (y12 + y23)*0.5f;\n        float x234 = (x23 + x34)*0.5f,    y234 = (y23 + y34)*0.5f;\n        float x1234 = (x123 + x234)*0.5f, y1234 = (y123 + y234)*0.5f;\n        ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);\n        ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);\n    }\n}\n\n// tess_tol is generally the same value you would find in ImGui::GetStyle().CurveTessellationTol\n// Because those ImXXX functions are lower-level than ImGui:: we cannot access this value automatically.\nImVec2 ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol)\n{\n    IM_ASSERT(tess_tol > 0.0f);\n    ImVec2 p_last = p1;\n    ImVec2 p_closest;\n    float p_closest_dist2 = FLT_MAX;\n    ImBezierCubicClosestPointCasteljauStep(p, p_closest, p_last, p_closest_dist2, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, tess_tol, 0);\n    return p_closest;\n}\n\nImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p)\n{\n    ImVec2 ap = p - a;\n    ImVec2 ab_dir = b - a;\n    float dot = ap.x * ab_dir.x + ap.y * ab_dir.y;\n    if (dot < 0.0f)\n        return a;\n    float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y;\n    if (dot > ab_len_sqr)\n        return b;\n    return a + ab_dir * dot / ab_len_sqr;\n}\n\nbool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)\n{\n    bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f;\n    bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f;\n    bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f;\n    return ((b1 == b2) && (b2 == b3));\n}\n\nvoid ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w)\n{\n    ImVec2 v0 = b - a;\n    ImVec2 v1 = c - a;\n    ImVec2 v2 = p - a;\n    const float denom = v0.x * v1.y - v1.x * v0.y;\n    out_v = (v2.x * v1.y - v1.x * v2.y) / denom;\n    out_w = (v0.x * v2.y - v2.x * v0.y) / denom;\n    out_u = 1.0f - out_v - out_w;\n}\n\nImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p)\n{\n    ImVec2 proj_ab = ImLineClosestPoint(a, b, p);\n    ImVec2 proj_bc = ImLineClosestPoint(b, c, p);\n    ImVec2 proj_ca = ImLineClosestPoint(c, a, p);\n    float dist2_ab = ImLengthSqr(p - proj_ab);\n    float dist2_bc = ImLengthSqr(p - proj_bc);\n    float dist2_ca = ImLengthSqr(p - proj_ca);\n    float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca));\n    if (m == dist2_ab)\n        return proj_ab;\n    if (m == dist2_bc)\n        return proj_bc;\n    return proj_ca;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] MISC HELPERS/UTILITIES (String, Format, Hash functions)\n//-----------------------------------------------------------------------------\n\n// Consider using _stricmp/_strnicmp under Windows or strcasecmp/strncasecmp. We don't actually use either ImStricmp/ImStrnicmp in the codebase any more.\nint ImStricmp(const char* str1, const char* str2)\n{\n    int d;\n    while ((d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; }\n    return d;\n}\n\nint ImStrnicmp(const char* str1, const char* str2, size_t count)\n{\n    int d = 0;\n    while (count > 0 && (d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; count--; }\n    return d;\n}\n\nvoid ImStrncpy(char* dst, const char* src, size_t count)\n{\n    if (count < 1)\n        return;\n    if (count > 1)\n        strncpy(dst, src, count - 1);\n    dst[count - 1] = 0;\n}\n\nchar* ImStrdup(const char* str)\n{\n    size_t len = strlen(str);\n    void* buf = IM_ALLOC(len + 1);\n    return (char*)memcpy(buf, (const void*)str, len + 1);\n}\n\nchar* ImStrdupcpy(char* dst, size_t* p_dst_size, const char* src)\n{\n    size_t dst_buf_size = p_dst_size ? *p_dst_size : strlen(dst) + 1;\n    size_t src_size = strlen(src) + 1;\n    if (dst_buf_size < src_size)\n    {\n        IM_FREE(dst);\n        dst = (char*)IM_ALLOC(src_size);\n        if (p_dst_size)\n            *p_dst_size = src_size;\n    }\n    return (char*)memcpy(dst, (const void*)src, src_size);\n}\n\nconst char* ImStrchrRange(const char* str, const char* str_end, char c)\n{\n    const char* p = (const char*)memchr(str, (int)c, str_end - str);\n    return p;\n}\n\nint ImStrlenW(const ImWchar* str)\n{\n    //return (int)wcslen((const wchar_t*)str);  // FIXME-OPT: Could use this when wchar_t are 16-bit\n    int n = 0;\n    while (*str++) n++;\n    return n;\n}\n\n// Find end-of-line. Return pointer will point to either first \\n, either str_end.\nconst char* ImStreolRange(const char* str, const char* str_end)\n{\n    const char* p = (const char*)memchr(str, '\\n', str_end - str);\n    return p ? p : str_end;\n}\n\nconst ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line\n{\n    while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\\n')\n        buf_mid_line--;\n    return buf_mid_line;\n}\n\nconst char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end)\n{\n    if (!needle_end)\n        needle_end = needle + strlen(needle);\n\n    const char un0 = (char)ImToUpper(*needle);\n    while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end))\n    {\n        if (ImToUpper(*haystack) == un0)\n        {\n            const char* b = needle + 1;\n            for (const char* a = haystack + 1; b < needle_end; a++, b++)\n                if (ImToUpper(*a) != ImToUpper(*b))\n                    break;\n            if (b == needle_end)\n                return haystack;\n        }\n        haystack++;\n    }\n    return NULL;\n}\n\n// Trim str by offsetting contents when there's leading data + writing a \\0 at the trailing position. We use this in situation where the cost is negligible.\nvoid ImStrTrimBlanks(char* buf)\n{\n    char* p = buf;\n    while (p[0] == ' ' || p[0] == '\\t')     // Leading blanks\n        p++;\n    char* p_start = p;\n    while (*p != 0)                         // Find end of string\n        p++;\n    while (p > p_start && (p[-1] == ' ' || p[-1] == '\\t'))  // Trailing blanks\n        p--;\n    if (p_start != buf)                     // Copy memory if we had leading blanks\n        memmove(buf, p_start, p - p_start);\n    buf[p - p_start] = 0;                   // Zero terminate\n}\n\nconst char* ImStrSkipBlank(const char* str)\n{\n    while (str[0] == ' ' || str[0] == '\\t')\n        str++;\n    return str;\n}\n\n// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size).\n// Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm.\n// B) When buf==NULL vsnprintf() will return the output size.\n#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n\n// We support stb_sprintf which is much faster (see: https://github.com/nothings/stb/blob/master/stb_sprintf.h)\n// You may set IMGUI_USE_STB_SPRINTF to use our default wrapper, or set IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n// and setup the wrapper yourself. (FIXME-OPT: Some of our high-level operations such as ImGuiTextBuffer::appendfv() are\n// designed using two-passes worst case, which probably could be improved using the stbsp_vsprintfcb() function.)\n#ifdef IMGUI_USE_STB_SPRINTF\n#ifndef IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION\n#define STB_SPRINTF_IMPLEMENTATION\n#endif\n#ifdef IMGUI_STB_SPRINTF_FILENAME\n#include IMGUI_STB_SPRINTF_FILENAME\n#else\n#include \"stb_sprintf.h\"\n#endif\n#endif // #ifdef IMGUI_USE_STB_SPRINTF\n\n#if defined(_MSC_VER) && !defined(vsnprintf)\n#define vsnprintf _vsnprintf\n#endif\n\nint ImFormatString(char* buf, size_t buf_size, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n#ifdef IMGUI_USE_STB_SPRINTF\n    int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);\n#else\n    int w = vsnprintf(buf, buf_size, fmt, args);\n#endif\n    va_end(args);\n    if (buf == NULL)\n        return w;\n    if (w == -1 || w >= (int)buf_size)\n        w = (int)buf_size - 1;\n    buf[w] = 0;\n    return w;\n}\n\nint ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)\n{\n#ifdef IMGUI_USE_STB_SPRINTF\n    int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);\n#else\n    int w = vsnprintf(buf, buf_size, fmt, args);\n#endif\n    if (buf == NULL)\n        return w;\n    if (w == -1 || w >= (int)buf_size)\n        w = (int)buf_size - 1;\n    buf[w] = 0;\n    return w;\n}\n#endif // #ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n\nvoid ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    ImFormatStringToTempBufferV(out_buf, out_buf_end, fmt, args);\n    va_end(args);\n}\n\nvoid ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args)\n{\n    ImGuiContext& g = *GImGui;\n    if (fmt[0] == '%' && fmt[1] == 's' && fmt[2] == 0)\n    {\n        const char* buf = va_arg(args, const char*); // Skip formatting when using \"%s\"\n        *out_buf = buf;\n        if (out_buf_end) { *out_buf_end = buf + strlen(buf); }\n    }\n    else if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 's' && fmt[4] == 0)\n    {\n        int buf_len = va_arg(args, int); // Skip formatting when using \"%.*s\"\n        const char* buf = va_arg(args, const char*);\n        *out_buf = buf;\n        *out_buf_end = buf + buf_len; // Disallow not passing 'out_buf_end' here. User is expected to use it.\n    }\n    else\n    {\n        int buf_len = ImFormatStringV(g.TempBuffer.Data, g.TempBuffer.Size, fmt, args);\n        *out_buf = g.TempBuffer.Data;\n        if (out_buf_end) { *out_buf_end = g.TempBuffer.Data + buf_len; }\n    }\n}\n\n// CRC32 needs a 1KB lookup table (not cache friendly)\n// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:\n// - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.\nstatic const ImU32 GCrc32LookupTable[256] =\n{\n    0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,\n    0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7,0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5,\n    0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B,0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59,\n    0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F,0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D,\n    0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433,0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01,\n    0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457,0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65,\n    0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB,0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9,\n    0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F,0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD,\n    0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683,0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1,\n    0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7,0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5,\n    0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B,0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79,\n    0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F,0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D,\n    0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713,0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21,\n    0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777,0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45,\n    0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB,0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9,\n    0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,\n};\n\n// Known size hash\n// It is ok to call ImHashData on a string with known length but the ### operator won't be supported.\n// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.\nImGuiID ImHashData(const void* data_p, size_t data_size, ImGuiID seed)\n{\n    ImU32 crc = ~seed;\n    const unsigned char* data = (const unsigned char*)data_p;\n    const ImU32* crc32_lut = GCrc32LookupTable;\n    while (data_size-- != 0)\n        crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];\n    return ~crc;\n}\n\n// Zero-terminated string hash, with support for ### to reset back to seed value\n// We support a syntax of \"label###id\" where only \"###id\" is included in the hash, and only \"label\" gets displayed.\n// Because this syntax is rarely used we are optimizing for the common case.\n// - If we reach ### in the string we discard the hash so far and reset to the seed.\n// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)\n// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.\nImGuiID ImHashStr(const char* data_p, size_t data_size, ImGuiID seed)\n{\n    seed = ~seed;\n    ImU32 crc = seed;\n    const unsigned char* data = (const unsigned char*)data_p;\n    const ImU32* crc32_lut = GCrc32LookupTable;\n    if (data_size != 0)\n    {\n        while (data_size-- != 0)\n        {\n            unsigned char c = *data++;\n            if (c == '#' && data_size >= 2 && data[0] == '#' && data[1] == '#')\n                crc = seed;\n            crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];\n        }\n    }\n    else\n    {\n        while (unsigned char c = *data++)\n        {\n            if (c == '#' && data[0] == '#' && data[1] == '#')\n                crc = seed;\n            crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];\n        }\n    }\n    return ~crc;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] MISC HELPERS/UTILITIES (File functions)\n//-----------------------------------------------------------------------------\n\n// Default file functions\n#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\n\nImFileHandle ImFileOpen(const char* filename, const char* mode)\n{\n#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(__CYGWIN__) && !defined(__GNUC__)\n    // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames.\n    // Previously we used ImTextCountCharsFromUtf8/ImTextStrFromUtf8 here but we now need to support ImWchar16 and ImWchar32!\n    const int filename_wsize = ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);\n    const int mode_wsize = ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);\n    ImGuiContext& g = *GImGui;\n    g.TempBuffer.reserve((filename_wsize + mode_wsize) * sizeof(wchar_t));\n    wchar_t* buf = (wchar_t*)(void*)g.TempBuffer.Data;\n    ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, (wchar_t*)&buf[0], filename_wsize);\n    ::MultiByteToWideChar(CP_UTF8, 0, mode, -1, (wchar_t*)&buf[filename_wsize], mode_wsize);\n    return ::_wfopen((const wchar_t*)&buf[0], (const wchar_t*)&buf[filename_wsize]);\n#else\n    return fopen(filename, mode);\n#endif\n}\n\n// We should in theory be using fseeko()/ftello() with off_t and _fseeki64()/_ftelli64() with __int64, waiting for the PR that does that in a very portable pre-C++11 zero-warnings way.\nbool    ImFileClose(ImFileHandle f)     { return fclose(f) == 0; }\nImU64   ImFileGetSize(ImFileHandle f)   { long off = 0, sz = 0; return ((off = ftell(f)) != -1 && !fseek(f, 0, SEEK_END) && (sz = ftell(f)) != -1 && !fseek(f, off, SEEK_SET)) ? (ImU64)sz : (ImU64)-1; }\nImU64   ImFileRead(void* data, ImU64 sz, ImU64 count, ImFileHandle f)           { return fread(data, (size_t)sz, (size_t)count, f); }\nImU64   ImFileWrite(const void* data, ImU64 sz, ImU64 count, ImFileHandle f)    { return fwrite(data, (size_t)sz, (size_t)count, f); }\n#endif // #ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\n\n// Helper: Load file content into memory\n// Memory allocated with IM_ALLOC(), must be freed by user using IM_FREE() == ImGui::MemFree()\n// This can't really be used with \"rt\" because fseek size won't match read size.\nvoid*   ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size, int padding_bytes)\n{\n    IM_ASSERT(filename && mode);\n    if (out_file_size)\n        *out_file_size = 0;\n\n    ImFileHandle f;\n    if ((f = ImFileOpen(filename, mode)) == NULL)\n        return NULL;\n\n    size_t file_size = (size_t)ImFileGetSize(f);\n    if (file_size == (size_t)-1)\n    {\n        ImFileClose(f);\n        return NULL;\n    }\n\n    void* file_data = IM_ALLOC(file_size + padding_bytes);\n    if (file_data == NULL)\n    {\n        ImFileClose(f);\n        return NULL;\n    }\n    if (ImFileRead(file_data, 1, file_size, f) != file_size)\n    {\n        ImFileClose(f);\n        IM_FREE(file_data);\n        return NULL;\n    }\n    if (padding_bytes > 0)\n        memset((void*)(((char*)file_data) + file_size), 0, (size_t)padding_bytes);\n\n    ImFileClose(f);\n    if (out_file_size)\n        *out_file_size = file_size;\n\n    return file_data;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] MISC HELPERS/UTILITIES (ImText* functions)\n//-----------------------------------------------------------------------------\n\nIM_MSVC_RUNTIME_CHECKS_OFF\n\n// Convert UTF-8 to 32-bit character, process single character input.\n// A nearly-branchless UTF-8 decoder, based on work of Christopher Wellons (https://github.com/skeeto/branchless-utf8).\n// We handle UTF-8 decoding error by skipping forward.\nint ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end)\n{\n    static const char lengths[32] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 };\n    static const int masks[]  = { 0x00, 0x7f, 0x1f, 0x0f, 0x07 };\n    static const uint32_t mins[] = { 0x400000, 0, 0x80, 0x800, 0x10000 };\n    static const int shiftc[] = { 0, 18, 12, 6, 0 };\n    static const int shifte[] = { 0, 6, 4, 2, 0 };\n    int len = lengths[*(const unsigned char*)in_text >> 3];\n    int wanted = len + (len ? 0 : 1);\n\n    if (in_text_end == NULL)\n        in_text_end = in_text + wanted; // Max length, nulls will be taken into account.\n\n    // Copy at most 'len' bytes, stop copying at 0 or past in_text_end. Branch predictor does a good job here,\n    // so it is fast even with excessive branching.\n    unsigned char s[4];\n    s[0] = in_text + 0 < in_text_end ? in_text[0] : 0;\n    s[1] = in_text + 1 < in_text_end ? in_text[1] : 0;\n    s[2] = in_text + 2 < in_text_end ? in_text[2] : 0;\n    s[3] = in_text + 3 < in_text_end ? in_text[3] : 0;\n\n    // Assume a four-byte character and load four bytes. Unused bits are shifted out.\n    *out_char  = (uint32_t)(s[0] & masks[len]) << 18;\n    *out_char |= (uint32_t)(s[1] & 0x3f) << 12;\n    *out_char |= (uint32_t)(s[2] & 0x3f) <<  6;\n    *out_char |= (uint32_t)(s[3] & 0x3f) <<  0;\n    *out_char >>= shiftc[len];\n\n    // Accumulate the various error conditions.\n    int e = 0;\n    e  = (*out_char < mins[len]) << 6; // non-canonical encoding\n    e |= ((*out_char >> 11) == 0x1b) << 7;  // surrogate half?\n    e |= (*out_char > IM_UNICODE_CODEPOINT_MAX) << 8;  // out of range?\n    e |= (s[1] & 0xc0) >> 2;\n    e |= (s[2] & 0xc0) >> 4;\n    e |= (s[3]       ) >> 6;\n    e ^= 0x2a; // top two bits of each tail byte correct?\n    e >>= shifte[len];\n\n    if (e)\n    {\n        // No bytes are consumed when *in_text == 0 || in_text == in_text_end.\n        // One byte is consumed in case of invalid first byte of in_text.\n        // All available bytes (at most `len` bytes) are consumed on incomplete/invalid second to last bytes.\n        // Invalid or incomplete input may consume less bytes than wanted, therefore every byte has to be inspected in s.\n        wanted = ImMin(wanted, !!s[0] + !!s[1] + !!s[2] + !!s[3]);\n        *out_char = IM_UNICODE_CODEPOINT_INVALID;\n    }\n\n    return wanted;\n}\n\nint ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining)\n{\n    ImWchar* buf_out = buf;\n    ImWchar* buf_end = buf + buf_size;\n    while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)\n    {\n        unsigned int c;\n        in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);\n        *buf_out++ = (ImWchar)c;\n    }\n    *buf_out = 0;\n    if (in_text_remaining)\n        *in_text_remaining = in_text;\n    return (int)(buf_out - buf);\n}\n\nint ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end)\n{\n    int char_count = 0;\n    while ((!in_text_end || in_text < in_text_end) && *in_text)\n    {\n        unsigned int c;\n        in_text += ImTextCharFromUtf8(&c, in_text, in_text_end);\n        char_count++;\n    }\n    return char_count;\n}\n\n// Based on stb_to_utf8() from github.com/nothings/stb/\nstatic inline int ImTextCharToUtf8_inline(char* buf, int buf_size, unsigned int c)\n{\n    if (c < 0x80)\n    {\n        buf[0] = (char)c;\n        return 1;\n    }\n    if (c < 0x800)\n    {\n        if (buf_size < 2) return 0;\n        buf[0] = (char)(0xc0 + (c >> 6));\n        buf[1] = (char)(0x80 + (c & 0x3f));\n        return 2;\n    }\n    if (c < 0x10000)\n    {\n        if (buf_size < 3) return 0;\n        buf[0] = (char)(0xe0 + (c >> 12));\n        buf[1] = (char)(0x80 + ((c >> 6) & 0x3f));\n        buf[2] = (char)(0x80 + ((c ) & 0x3f));\n        return 3;\n    }\n    if (c <= 0x10FFFF)\n    {\n        if (buf_size < 4) return 0;\n        buf[0] = (char)(0xf0 + (c >> 18));\n        buf[1] = (char)(0x80 + ((c >> 12) & 0x3f));\n        buf[2] = (char)(0x80 + ((c >> 6) & 0x3f));\n        buf[3] = (char)(0x80 + ((c ) & 0x3f));\n        return 4;\n    }\n    // Invalid code point, the max unicode is 0x10FFFF\n    return 0;\n}\n\nconst char* ImTextCharToUtf8(char out_buf[5], unsigned int c)\n{\n    int count = ImTextCharToUtf8_inline(out_buf, 5, c);\n    out_buf[count] = 0;\n    return out_buf;\n}\n\n// Not optimal but we very rarely use this function.\nint ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end)\n{\n    unsigned int unused = 0;\n    return ImTextCharFromUtf8(&unused, in_text, in_text_end);\n}\n\nstatic inline int ImTextCountUtf8BytesFromChar(unsigned int c)\n{\n    if (c < 0x80) return 1;\n    if (c < 0x800) return 2;\n    if (c < 0x10000) return 3;\n    if (c <= 0x10FFFF) return 4;\n    return 3;\n}\n\nint ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end)\n{\n    char* buf_p = out_buf;\n    const char* buf_end = out_buf + out_buf_size;\n    while (buf_p < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text)\n    {\n        unsigned int c = (unsigned int)(*in_text++);\n        if (c < 0x80)\n            *buf_p++ = (char)c;\n        else\n            buf_p += ImTextCharToUtf8_inline(buf_p, (int)(buf_end - buf_p - 1), c);\n    }\n    *buf_p = 0;\n    return (int)(buf_p - out_buf);\n}\n\nint ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end)\n{\n    int bytes_count = 0;\n    while ((!in_text_end || in_text < in_text_end) && *in_text)\n    {\n        unsigned int c = (unsigned int)(*in_text++);\n        if (c < 0x80)\n            bytes_count++;\n        else\n            bytes_count += ImTextCountUtf8BytesFromChar(c);\n    }\n    return bytes_count;\n}\n\nconst char* ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr)\n{\n    while (in_text_curr > in_text_start)\n    {\n        in_text_curr--;\n        if ((*in_text_curr & 0xC0) != 0x80)\n            return in_text_curr;\n    }\n    return in_text_start;\n}\n\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n//-----------------------------------------------------------------------------\n// [SECTION] MISC HELPERS/UTILITIES (Color functions)\n// Note: The Convert functions are early design which are not consistent with other API.\n//-----------------------------------------------------------------------------\n\nIMGUI_API ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b)\n{\n    float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f;\n    int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t);\n    int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t);\n    int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t);\n    return IM_COL32(r, g, b, 0xFF);\n}\n\nImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in)\n{\n    float s = 1.0f / 255.0f;\n    return ImVec4(\n        ((in >> IM_COL32_R_SHIFT) & 0xFF) * s,\n        ((in >> IM_COL32_G_SHIFT) & 0xFF) * s,\n        ((in >> IM_COL32_B_SHIFT) & 0xFF) * s,\n        ((in >> IM_COL32_A_SHIFT) & 0xFF) * s);\n}\n\nImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in)\n{\n    ImU32 out;\n    out  = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT;\n    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT;\n    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT;\n    out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT;\n    return out;\n}\n\n// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592\n// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv\nvoid ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v)\n{\n    float K = 0.f;\n    if (g < b)\n    {\n        ImSwap(g, b);\n        K = -1.f;\n    }\n    if (r < g)\n    {\n        ImSwap(r, g);\n        K = -2.f / 6.f - K;\n    }\n\n    const float chroma = r - (g < b ? g : b);\n    out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f));\n    out_s = chroma / (r + 1e-20f);\n    out_v = r;\n}\n\n// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593\n// also http://en.wikipedia.org/wiki/HSL_and_HSV\nvoid ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b)\n{\n    if (s == 0.0f)\n    {\n        // gray\n        out_r = out_g = out_b = v;\n        return;\n    }\n\n    h = ImFmod(h, 1.0f) / (60.0f / 360.0f);\n    int   i = (int)h;\n    float f = h - (float)i;\n    float p = v * (1.0f - s);\n    float q = v * (1.0f - s * f);\n    float t = v * (1.0f - s * (1.0f - f));\n\n    switch (i)\n    {\n    case 0: out_r = v; out_g = t; out_b = p; break;\n    case 1: out_r = q; out_g = v; out_b = p; break;\n    case 2: out_r = p; out_g = v; out_b = t; break;\n    case 3: out_r = p; out_g = q; out_b = v; break;\n    case 4: out_r = t; out_g = p; out_b = v; break;\n    case 5: default: out_r = v; out_g = p; out_b = q; break;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiStorage\n// Helper: Key->value storage\n//-----------------------------------------------------------------------------\n\n// std::lower_bound but without the bullshit\nstatic ImGuiStorage::ImGuiStoragePair* LowerBound(ImVector<ImGuiStorage::ImGuiStoragePair>& data, ImGuiID key)\n{\n    ImGuiStorage::ImGuiStoragePair* first = data.Data;\n    ImGuiStorage::ImGuiStoragePair* last = data.Data + data.Size;\n    size_t count = (size_t)(last - first);\n    while (count > 0)\n    {\n        size_t count2 = count >> 1;\n        ImGuiStorage::ImGuiStoragePair* mid = first + count2;\n        if (mid->key < key)\n        {\n            first = ++mid;\n            count -= count2 + 1;\n        }\n        else\n        {\n            count = count2;\n        }\n    }\n    return first;\n}\n\n// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.\nvoid ImGuiStorage::BuildSortByKey()\n{\n    struct StaticFunc\n    {\n        static int IMGUI_CDECL PairComparerByID(const void* lhs, const void* rhs)\n        {\n            // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that.\n            if (((const ImGuiStoragePair*)lhs)->key > ((const ImGuiStoragePair*)rhs)->key) return +1;\n            if (((const ImGuiStoragePair*)lhs)->key < ((const ImGuiStoragePair*)rhs)->key) return -1;\n            return 0;\n        }\n    };\n    ImQsort(Data.Data, (size_t)Data.Size, sizeof(ImGuiStoragePair), StaticFunc::PairComparerByID);\n}\n\nint ImGuiStorage::GetInt(ImGuiID key, int default_val) const\n{\n    ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);\n    if (it == Data.end() || it->key != key)\n        return default_val;\n    return it->val_i;\n}\n\nbool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const\n{\n    return GetInt(key, default_val ? 1 : 0) != 0;\n}\n\nfloat ImGuiStorage::GetFloat(ImGuiID key, float default_val) const\n{\n    ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);\n    if (it == Data.end() || it->key != key)\n        return default_val;\n    return it->val_f;\n}\n\nvoid* ImGuiStorage::GetVoidPtr(ImGuiID key) const\n{\n    ImGuiStoragePair* it = LowerBound(const_cast<ImVector<ImGuiStoragePair>&>(Data), key);\n    if (it == Data.end() || it->key != key)\n        return NULL;\n    return it->val_p;\n}\n\n// References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.\nint* ImGuiStorage::GetIntRef(ImGuiID key, int default_val)\n{\n    ImGuiStoragePair* it = LowerBound(Data, key);\n    if (it == Data.end() || it->key != key)\n        it = Data.insert(it, ImGuiStoragePair(key, default_val));\n    return &it->val_i;\n}\n\nbool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val)\n{\n    return (bool*)GetIntRef(key, default_val ? 1 : 0);\n}\n\nfloat* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val)\n{\n    ImGuiStoragePair* it = LowerBound(Data, key);\n    if (it == Data.end() || it->key != key)\n        it = Data.insert(it, ImGuiStoragePair(key, default_val));\n    return &it->val_f;\n}\n\nvoid** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val)\n{\n    ImGuiStoragePair* it = LowerBound(Data, key);\n    if (it == Data.end() || it->key != key)\n        it = Data.insert(it, ImGuiStoragePair(key, default_val));\n    return &it->val_p;\n}\n\n// FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame)\nvoid ImGuiStorage::SetInt(ImGuiID key, int val)\n{\n    ImGuiStoragePair* it = LowerBound(Data, key);\n    if (it == Data.end() || it->key != key)\n        Data.insert(it, ImGuiStoragePair(key, val));\n    else\n        it->val_i = val;\n}\n\nvoid ImGuiStorage::SetBool(ImGuiID key, bool val)\n{\n    SetInt(key, val ? 1 : 0);\n}\n\nvoid ImGuiStorage::SetFloat(ImGuiID key, float val)\n{\n    ImGuiStoragePair* it = LowerBound(Data, key);\n    if (it == Data.end() || it->key != key)\n        Data.insert(it, ImGuiStoragePair(key, val));\n    else\n        it->val_f = val;\n}\n\nvoid ImGuiStorage::SetVoidPtr(ImGuiID key, void* val)\n{\n    ImGuiStoragePair* it = LowerBound(Data, key);\n    if (it == Data.end() || it->key != key)\n        Data.insert(it, ImGuiStoragePair(key, val));\n    else\n        it->val_p = val;\n}\n\nvoid ImGuiStorage::SetAllInt(int v)\n{\n    for (int i = 0; i < Data.Size; i++)\n        Data[i].val_i = v;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiTextFilter\n//-----------------------------------------------------------------------------\n\n// Helper: Parse and apply text filters. In format \"aaaaa[,bbbb][,ccccc]\"\nImGuiTextFilter::ImGuiTextFilter(const char* default_filter) //-V1077\n{\n    InputBuf[0] = 0;\n    CountGrep = 0;\n    if (default_filter)\n    {\n        ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf));\n        Build();\n    }\n}\n\nbool ImGuiTextFilter::Draw(const char* label, float width)\n{\n    if (width != 0.0f)\n        ImGui::SetNextItemWidth(width);\n    bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf));\n    if (value_changed)\n        Build();\n    return value_changed;\n}\n\nvoid ImGuiTextFilter::ImGuiTextRange::split(char separator, ImVector<ImGuiTextRange>* out) const\n{\n    out->resize(0);\n    const char* wb = b;\n    const char* we = wb;\n    while (we < e)\n    {\n        if (*we == separator)\n        {\n            out->push_back(ImGuiTextRange(wb, we));\n            wb = we + 1;\n        }\n        we++;\n    }\n    if (wb != we)\n        out->push_back(ImGuiTextRange(wb, we));\n}\n\nvoid ImGuiTextFilter::Build()\n{\n    Filters.resize(0);\n    ImGuiTextRange input_range(InputBuf, InputBuf + strlen(InputBuf));\n    input_range.split(',', &Filters);\n\n    CountGrep = 0;\n    for (ImGuiTextRange& f : Filters)\n    {\n        while (f.b < f.e && ImCharIsBlankA(f.b[0]))\n            f.b++;\n        while (f.e > f.b && ImCharIsBlankA(f.e[-1]))\n            f.e--;\n        if (f.empty())\n            continue;\n        if (f.b[0] != '-')\n            CountGrep += 1;\n    }\n}\n\nbool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const\n{\n    if (Filters.empty())\n        return true;\n\n    if (text == NULL)\n        text = \"\";\n\n    for (const ImGuiTextRange& f : Filters)\n    {\n        if (f.empty())\n            continue;\n        if (f.b[0] == '-')\n        {\n            // Subtract\n            if (ImStristr(text, text_end, f.b + 1, f.e) != NULL)\n                return false;\n        }\n        else\n        {\n            // Grep\n            if (ImStristr(text, text_end, f.b, f.e) != NULL)\n                return true;\n        }\n    }\n\n    // Implicit * grep\n    if (CountGrep == 0)\n        return true;\n\n    return false;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiTextBuffer, ImGuiTextIndex\n//-----------------------------------------------------------------------------\n\n// On some platform vsnprintf() takes va_list by reference and modifies it.\n// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it.\n#ifndef va_copy\n#if defined(__GNUC__) || defined(__clang__)\n#define va_copy(dest, src) __builtin_va_copy(dest, src)\n#else\n#define va_copy(dest, src) (dest = src)\n#endif\n#endif\n\nchar ImGuiTextBuffer::EmptyString[1] = { 0 };\n\nvoid ImGuiTextBuffer::append(const char* str, const char* str_end)\n{\n    int len = str_end ? (int)(str_end - str) : (int)strlen(str);\n\n    // Add zero-terminator the first time\n    const int write_off = (Buf.Size != 0) ? Buf.Size : 1;\n    const int needed_sz = write_off + len;\n    if (write_off + len >= Buf.Capacity)\n    {\n        int new_capacity = Buf.Capacity * 2;\n        Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);\n    }\n\n    Buf.resize(needed_sz);\n    memcpy(&Buf[write_off - 1], str, (size_t)len);\n    Buf[write_off - 1 + len] = 0;\n}\n\nvoid ImGuiTextBuffer::appendf(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    appendfv(fmt, args);\n    va_end(args);\n}\n\n// Helper: Text buffer for logging/accumulating text\nvoid ImGuiTextBuffer::appendfv(const char* fmt, va_list args)\n{\n    va_list args_copy;\n    va_copy(args_copy, args);\n\n    int len = ImFormatStringV(NULL, 0, fmt, args);         // FIXME-OPT: could do a first pass write attempt, likely successful on first pass.\n    if (len <= 0)\n    {\n        va_end(args_copy);\n        return;\n    }\n\n    // Add zero-terminator the first time\n    const int write_off = (Buf.Size != 0) ? Buf.Size : 1;\n    const int needed_sz = write_off + len;\n    if (write_off + len >= Buf.Capacity)\n    {\n        int new_capacity = Buf.Capacity * 2;\n        Buf.reserve(needed_sz > new_capacity ? needed_sz : new_capacity);\n    }\n\n    Buf.resize(needed_sz);\n    ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy);\n    va_end(args_copy);\n}\n\nvoid ImGuiTextIndex::append(const char* base, int old_size, int new_size)\n{\n    IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset);\n    if (old_size == new_size)\n        return;\n    if (EndOffset == 0 || base[EndOffset - 1] == '\\n')\n        LineOffsets.push_back(EndOffset);\n    const char* base_end = base + new_size;\n    for (const char* p = base + old_size; (p = (const char*)memchr(p, '\\n', base_end - p)) != 0; )\n        if (++p < base_end) // Don't push a trailing offset on last \\n\n            LineOffsets.push_back((int)(intptr_t)(p - base));\n    EndOffset = ImMax(EndOffset, new_size);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiListClipper\n//-----------------------------------------------------------------------------\n\n// FIXME-TABLE: This prevents us from using ImGuiListClipper _inside_ a table cell.\n// The problem we have is that without a Begin/End scheme for rows using the clipper is ambiguous.\nstatic bool GetSkipItemForListClipping()\n{\n    ImGuiContext& g = *GImGui;\n    return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n// Legacy helper to calculate coarse clipping of large list of evenly sized items.\n// This legacy API is not ideal because it assumes we will return a single contiguous rectangle.\n// Prefer using ImGuiListClipper which can returns non-contiguous ranges.\nvoid ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (g.LogEnabled)\n    {\n        // If logging is active, do not perform any clipping\n        *out_items_display_start = 0;\n        *out_items_display_end = items_count;\n        return;\n    }\n    if (GetSkipItemForListClipping())\n    {\n        *out_items_display_start = *out_items_display_end = 0;\n        return;\n    }\n\n    // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect\n    // We don't include g.NavId's rectangle in there (unless g.NavJustMovedToId is set) because the rectangle enlargement can get costly.\n    ImRect rect = window->ClipRect;\n    if (g.NavMoveScoringItems)\n        rect.Add(g.NavScoringNoClipRect);\n    if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)\n        rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel\n\n    const ImVec2 pos = window->DC.CursorPos;\n    int start = (int)((rect.Min.y - pos.y) / items_height);\n    int end = (int)((rect.Max.y - pos.y) / items_height);\n\n    // When performing a navigation request, ensure we have one item extra in the direction we are moving to\n    // FIXME: Verify this works with tabbing\n    const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);\n    if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up)\n        start--;\n    if (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down)\n        end++;\n\n    start = ImClamp(start, 0, items_count);\n    end = ImClamp(end + 1, start, items_count);\n    *out_items_display_start = start;\n    *out_items_display_end = end;\n}\n#endif\n\nstatic void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>& ranges, int offset = 0)\n{\n    if (ranges.Size - offset <= 1)\n        return;\n\n    // Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)\n    for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end)\n        for (int i = offset; i < sort_end + offset; ++i)\n            if (ranges[i].Min > ranges[i + 1].Min)\n                ImSwap(ranges[i], ranges[i + 1]);\n\n    // Now fuse ranges together as much as possible.\n    for (int i = 1 + offset; i < ranges.Size; i++)\n    {\n        IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert);\n        if (ranges[i - 1].Max < ranges[i].Min)\n            continue;\n        ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min);\n        ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max);\n        ranges.erase(ranges.Data + i);\n        i--;\n    }\n}\n\nstatic void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height)\n{\n    // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.\n    // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.\n    // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek?\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    float off_y = pos_y - window->DC.CursorPos.y;\n    window->DC.CursorPos.y = pos_y;\n    window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y);\n    window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height;  // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage.\n    window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y);      // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.\n    if (ImGuiOldColumns* columns = window->DC.CurrentColumns)\n        columns->LineMinY = window->DC.CursorPos.y;                         // Setting this so that cell Y position are set properly\n    if (ImGuiTable* table = g.CurrentTable)\n    {\n        if (table->IsInsideRow)\n            ImGui::TableEndRow(table);\n        table->RowPosY2 = window->DC.CursorPos.y;\n        const int row_increase = (int)((off_y / line_height) + 0.5f);\n        //table->CurrentRow += row_increase; // Can't do without fixing TableEndRow()\n        table->RowBgColorCounter += row_increase;\n    }\n}\n\nstatic void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n)\n{\n    // StartPosY starts from ItemsFrozen hence the subtraction\n    // Perform the add and multiply with double to allow seeking through larger ranges\n    ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;\n    float pos_y = (float)((double)clipper->StartPosY + data->LossynessOffset + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight);\n    ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight);\n}\n\nImGuiListClipper::ImGuiListClipper()\n{\n    memset(this, 0, sizeof(*this));\n}\n\nImGuiListClipper::~ImGuiListClipper()\n{\n    End();\n}\n\nvoid ImGuiListClipper::Begin(int items_count, float items_height)\n{\n    if (Ctx == NULL)\n        Ctx = ImGui::GetCurrentContext();\n\n    ImGuiContext& g = *Ctx;\n    ImGuiWindow* window = g.CurrentWindow;\n    IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: Begin(%d,%.2f) in '%s'\\n\", items_count, items_height, window->Name);\n\n    if (ImGuiTable* table = g.CurrentTable)\n        if (table->IsInsideRow)\n            ImGui::TableEndRow(table);\n\n    StartPosY = window->DC.CursorPos.y;\n    ItemsHeight = items_height;\n    ItemsCount = items_count;\n    DisplayStart = -1;\n    DisplayEnd = 0;\n\n    // Acquire temporary buffer\n    if (++g.ClipperTempDataStacked > g.ClipperTempData.Size)\n        g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData());\n    ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];\n    data->Reset(this);\n    data->LossynessOffset = window->DC.CursorStartPosLossyness.y;\n    TempData = data;\n}\n\nvoid ImGuiListClipper::End()\n{\n    if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)\n    {\n        // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.\n        ImGuiContext& g = *Ctx;\n        IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: End() in '%s'\\n\", g.CurrentWindow->Name);\n        if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)\n            ImGuiListClipper_SeekCursorForItem(this, ItemsCount);\n\n        // Restore temporary buffer and fix back pointers which may be invalidated when nesting\n        IM_ASSERT(data->ListClipper == this);\n        data->StepNo = data->Ranges.Size;\n        if (--g.ClipperTempDataStacked > 0)\n        {\n            data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];\n            data->ListClipper->TempData = data;\n        }\n        TempData = NULL;\n    }\n    ItemsCount = -1;\n}\n\nvoid ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)\n{\n    ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;\n    IM_ASSERT(DisplayStart < 0); // Only allowed after Begin() and if there has not been a specified range yet.\n    IM_ASSERT(item_begin <= item_end);\n    if (item_begin < item_end)\n        data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end));\n}\n\nstatic bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)\n{\n    ImGuiContext& g = *clipper->Ctx;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;\n    IM_ASSERT(data != NULL && \"Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?\");\n\n    ImGuiTable* table = g.CurrentTable;\n    if (table && table->IsInsideRow)\n        ImGui::TableEndRow(table);\n\n    // No items\n    if (clipper->ItemsCount == 0 || GetSkipItemForListClipping())\n        return false;\n\n    // While we are in frozen row state, keep displaying items one by one, unclipped\n    // FIXME: Could be stored as a table-agnostic state.\n    if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows)\n    {\n        clipper->DisplayStart = data->ItemsFrozen;\n        clipper->DisplayEnd = ImMin(data->ItemsFrozen + 1, clipper->ItemsCount);\n        if (clipper->DisplayStart < clipper->DisplayEnd)\n            data->ItemsFrozen++;\n        return true;\n    }\n\n    // Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)\n    bool calc_clipping = false;\n    if (data->StepNo == 0)\n    {\n        clipper->StartPosY = window->DC.CursorPos.y;\n        if (clipper->ItemsHeight <= 0.0f)\n        {\n            // Submit the first item (or range) so we can measure its height (generally the first range is 0..1)\n            data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1));\n            clipper->DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen);\n            clipper->DisplayEnd = ImMin(data->Ranges[0].Max, clipper->ItemsCount);\n            data->StepNo = 1;\n            return true;\n        }\n        calc_clipping = true;   // If on the first step with known item height, calculate clipping.\n    }\n\n    // Step 1: Let the clipper infer height from first range\n    if (clipper->ItemsHeight <= 0.0f)\n    {\n        IM_ASSERT(data->StepNo == 1);\n        if (table)\n            IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y);\n\n        clipper->ItemsHeight = (window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart);\n        bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y);\n        if (affected_by_floating_point_precision)\n            clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries.\n\n        IM_ASSERT(clipper->ItemsHeight > 0.0f && \"Unable to calculate item height! First item hasn't moved the cursor vertically!\");\n        calc_clipping = true;   // If item height had to be calculated, calculate clipping afterwards.\n    }\n\n    // Step 0 or 1: Calculate the actual ranges of visible elements.\n    const int already_submitted = clipper->DisplayEnd;\n    if (calc_clipping)\n    {\n        if (g.LogEnabled)\n        {\n            // If logging is active, do not perform any clipping\n            data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, clipper->ItemsCount));\n        }\n        else\n        {\n            // Add range selected to be included for navigation\n            const bool is_nav_request = (g.NavMoveScoringItems && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);\n            if (is_nav_request)\n                data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0));\n            if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && g.NavTabbingDir == -1)\n                data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount));\n\n            // Add focused/active item\n            ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]);\n            if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)\n                data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0));\n\n            // Add visible range\n            const int off_min = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;\n            const int off_max = (is_nav_request && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;\n            data->Ranges.push_back(ImGuiListClipperRange::FromPositions(window->ClipRect.Min.y, window->ClipRect.Max.y, off_min, off_max));\n        }\n\n        // Convert position ranges to item index ranges\n        // - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping.\n        // - Due to how Selectable extra padding they tend to be \"unaligned\" with exact unit in the item list,\n        //   which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.\n        for (ImGuiListClipperRange& range : data->Ranges)\n            if (range.PosToIndexConvert)\n            {\n                int m1 = (int)(((double)range.Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight);\n                int m2 = (int)((((double)range.Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f);\n                range.Min = ImClamp(already_submitted + m1 + range.PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1);\n                range.Max = ImClamp(already_submitted + m2 + range.PosToIndexOffsetMax, range.Min + 1, clipper->ItemsCount);\n                range.PosToIndexConvert = false;\n            }\n        ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo);\n    }\n\n    // Step 0+ (if item height is given in advance) or 1+: Display the next range in line.\n    while (data->StepNo < data->Ranges.Size)\n    {\n        clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);\n        clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount);\n        if (clipper->DisplayStart > already_submitted) //-V1051\n            ImGuiListClipper_SeekCursorForItem(clipper, clipper->DisplayStart);\n        data->StepNo++;\n        if (clipper->DisplayStart == clipper->DisplayEnd && data->StepNo < data->Ranges.Size)\n            continue;\n        return true;\n    }\n\n    // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),\n    // Advance the cursor to the end of the list and then returns 'false' to end the loop.\n    if (clipper->ItemsCount < INT_MAX)\n        ImGuiListClipper_SeekCursorForItem(clipper, clipper->ItemsCount);\n\n    return false;\n}\n\nbool ImGuiListClipper::Step()\n{\n    ImGuiContext& g = *Ctx;\n    bool need_items_height = (ItemsHeight <= 0.0f);\n    bool ret = ImGuiListClipper_StepInternal(this);\n    if (ret && (DisplayStart == DisplayEnd))\n        ret = false;\n    if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false)\n        IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: Step(): inside frozen table row.\\n\");\n    if (need_items_height && ItemsHeight > 0.0f)\n        IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: Step(): computed ItemsHeight: %.2f.\\n\", ItemsHeight);\n    if (ret)\n    {\n        IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: Step(): display %d to %d.\\n\", DisplayStart, DisplayEnd);\n    }\n    else\n    {\n        IMGUI_DEBUG_LOG_CLIPPER(\"Clipper: Step(): End.\\n\");\n        End();\n    }\n    return ret;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] STYLING\n//-----------------------------------------------------------------------------\n\nImGuiStyle& ImGui::GetStyle()\n{\n    IM_ASSERT(GImGui != NULL && \"No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?\");\n    return GImGui->Style;\n}\n\nImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul)\n{\n    ImGuiStyle& style = GImGui->Style;\n    ImVec4 c = style.Colors[idx];\n    c.w *= style.Alpha * alpha_mul;\n    return ColorConvertFloat4ToU32(c);\n}\n\nImU32 ImGui::GetColorU32(const ImVec4& col)\n{\n    ImGuiStyle& style = GImGui->Style;\n    ImVec4 c = col;\n    c.w *= style.Alpha;\n    return ColorConvertFloat4ToU32(c);\n}\n\nconst ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx)\n{\n    ImGuiStyle& style = GImGui->Style;\n    return style.Colors[idx];\n}\n\nImU32 ImGui::GetColorU32(ImU32 col)\n{\n    ImGuiStyle& style = GImGui->Style;\n    if (style.Alpha >= 1.0f)\n        return col;\n    ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT;\n    a = (ImU32)(a * style.Alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range.\n    return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT);\n}\n\n// FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32\nvoid ImGui::PushStyleColor(ImGuiCol idx, ImU32 col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiColorMod backup;\n    backup.Col = idx;\n    backup.BackupValue = g.Style.Colors[idx];\n    g.ColorStack.push_back(backup);\n    g.Style.Colors[idx] = ColorConvertU32ToFloat4(col);\n}\n\nvoid ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiColorMod backup;\n    backup.Col = idx;\n    backup.BackupValue = g.Style.Colors[idx];\n    g.ColorStack.push_back(backup);\n    g.Style.Colors[idx] = col;\n}\n\nvoid ImGui::PopStyleColor(int count)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.ColorStack.Size < count)\n    {\n        IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, \"Calling PopStyleColor() too many times: stack underflow.\");\n        count = g.ColorStack.Size;\n    }\n    while (count > 0)\n    {\n        ImGuiColorMod& backup = g.ColorStack.back();\n        g.Style.Colors[backup.Col] = backup.BackupValue;\n        g.ColorStack.pop_back();\n        count--;\n    }\n}\n\nstatic const ImGuiDataVarInfo GStyleVarInfo[] =\n{\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, Alpha) },                 // ImGuiStyleVar_Alpha\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DisabledAlpha) },         // ImGuiStyleVar_DisabledAlpha\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowPadding) },         // ImGuiStyleVar_WindowPadding\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowRounding) },        // ImGuiStyleVar_WindowRounding\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, WindowBorderSize) },      // ImGuiStyleVar_WindowBorderSize\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowMinSize) },         // ImGuiStyleVar_WindowMinSize\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, WindowTitleAlign) },      // ImGuiStyleVar_WindowTitleAlign\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildRounding) },         // ImGuiStyleVar_ChildRounding\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ChildBorderSize) },       // ImGuiStyleVar_ChildBorderSize\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupRounding) },         // ImGuiStyleVar_PopupRounding\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, PopupBorderSize) },       // ImGuiStyleVar_PopupBorderSize\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, FramePadding) },          // ImGuiStyleVar_FramePadding\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameRounding) },         // ImGuiStyleVar_FrameRounding\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, FrameBorderSize) },       // ImGuiStyleVar_FrameBorderSize\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemSpacing) },           // ImGuiStyleVar_ItemSpacing\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ItemInnerSpacing) },      // ImGuiStyleVar_ItemInnerSpacing\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, IndentSpacing) },         // ImGuiStyleVar_IndentSpacing\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, CellPadding) },           // ImGuiStyleVar_CellPadding\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarSize) },         // ImGuiStyleVar_ScrollbarSize\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, ScrollbarRounding) },     // ImGuiStyleVar_ScrollbarRounding\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabMinSize) },           // ImGuiStyleVar_GrabMinSize\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, GrabRounding) },          // ImGuiStyleVar_GrabRounding\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabRounding) },           // ImGuiStyleVar_TabRounding\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, TabBarBorderSize) },      // ImGuiStyleVar_TabBarBorderSize\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, ButtonTextAlign) },       // ImGuiStyleVar_ButtonTextAlign\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SelectableTextAlign) },   // ImGuiStyleVar_SelectableTextAlign\n    { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)},// ImGuiStyleVar_SeparatorTextBorderSize\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) },    // ImGuiStyleVar_SeparatorTextAlign\n    { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) },  // ImGuiStyleVar_SeparatorTextPadding\n};\n\nconst ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx)\n{\n    IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT);\n    IM_STATIC_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT);\n    return &GStyleVarInfo[idx];\n}\n\nvoid ImGui::PushStyleVar(ImGuiStyleVar idx, float val)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);\n    if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1)\n    {\n        float* pvar = (float*)var_info->GetVarPtr(&g.Style);\n        g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));\n        *pvar = val;\n        return;\n    }\n    IM_ASSERT_USER_ERROR(0, \"Called PushStyleVar() variant with wrong type!\");\n}\n\nvoid ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiDataVarInfo* var_info = GetStyleVarInfo(idx);\n    if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2)\n    {\n        ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style);\n        g.StyleVarStack.push_back(ImGuiStyleMod(idx, *pvar));\n        *pvar = val;\n        return;\n    }\n    IM_ASSERT_USER_ERROR(0, \"Called PushStyleVar() variant with wrong type!\");\n}\n\nvoid ImGui::PopStyleVar(int count)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.StyleVarStack.Size < count)\n    {\n        IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, \"Calling PopStyleVar() too many times: stack underflow.\");\n        count = g.StyleVarStack.Size;\n    }\n    while (count > 0)\n    {\n        // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it.\n        ImGuiStyleMod& backup = g.StyleVarStack.back();\n        const ImGuiDataVarInfo* info = GetStyleVarInfo(backup.VarIdx);\n        void* data = info->GetVarPtr(&g.Style);\n        if (info->Type == ImGuiDataType_Float && info->Count == 1)      { ((float*)data)[0] = backup.BackupFloat[0]; }\n        else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; }\n        g.StyleVarStack.pop_back();\n        count--;\n    }\n}\n\nconst char* ImGui::GetStyleColorName(ImGuiCol idx)\n{\n    // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\\1: return \"\\1\";\n    switch (idx)\n    {\n    case ImGuiCol_Text: return \"Text\";\n    case ImGuiCol_TextDisabled: return \"TextDisabled\";\n    case ImGuiCol_WindowBg: return \"WindowBg\";\n    case ImGuiCol_ChildBg: return \"ChildBg\";\n    case ImGuiCol_PopupBg: return \"PopupBg\";\n    case ImGuiCol_Border: return \"Border\";\n    case ImGuiCol_BorderShadow: return \"BorderShadow\";\n    case ImGuiCol_FrameBg: return \"FrameBg\";\n    case ImGuiCol_FrameBgHovered: return \"FrameBgHovered\";\n    case ImGuiCol_FrameBgActive: return \"FrameBgActive\";\n    case ImGuiCol_TitleBg: return \"TitleBg\";\n    case ImGuiCol_TitleBgActive: return \"TitleBgActive\";\n    case ImGuiCol_TitleBgCollapsed: return \"TitleBgCollapsed\";\n    case ImGuiCol_MenuBarBg: return \"MenuBarBg\";\n    case ImGuiCol_ScrollbarBg: return \"ScrollbarBg\";\n    case ImGuiCol_ScrollbarGrab: return \"ScrollbarGrab\";\n    case ImGuiCol_ScrollbarGrabHovered: return \"ScrollbarGrabHovered\";\n    case ImGuiCol_ScrollbarGrabActive: return \"ScrollbarGrabActive\";\n    case ImGuiCol_CheckMark: return \"CheckMark\";\n    case ImGuiCol_SliderGrab: return \"SliderGrab\";\n    case ImGuiCol_SliderGrabActive: return \"SliderGrabActive\";\n    case ImGuiCol_Button: return \"Button\";\n    case ImGuiCol_ButtonHovered: return \"ButtonHovered\";\n    case ImGuiCol_ButtonActive: return \"ButtonActive\";\n    case ImGuiCol_Header: return \"Header\";\n    case ImGuiCol_HeaderHovered: return \"HeaderHovered\";\n    case ImGuiCol_HeaderActive: return \"HeaderActive\";\n    case ImGuiCol_Separator: return \"Separator\";\n    case ImGuiCol_SeparatorHovered: return \"SeparatorHovered\";\n    case ImGuiCol_SeparatorActive: return \"SeparatorActive\";\n    case ImGuiCol_ResizeGrip: return \"ResizeGrip\";\n    case ImGuiCol_ResizeGripHovered: return \"ResizeGripHovered\";\n    case ImGuiCol_ResizeGripActive: return \"ResizeGripActive\";\n    case ImGuiCol_Tab: return \"Tab\";\n    case ImGuiCol_TabHovered: return \"TabHovered\";\n    case ImGuiCol_TabActive: return \"TabActive\";\n    case ImGuiCol_TabUnfocused: return \"TabUnfocused\";\n    case ImGuiCol_TabUnfocusedActive: return \"TabUnfocusedActive\";\n    case ImGuiCol_PlotLines: return \"PlotLines\";\n    case ImGuiCol_PlotLinesHovered: return \"PlotLinesHovered\";\n    case ImGuiCol_PlotHistogram: return \"PlotHistogram\";\n    case ImGuiCol_PlotHistogramHovered: return \"PlotHistogramHovered\";\n    case ImGuiCol_TableHeaderBg: return \"TableHeaderBg\";\n    case ImGuiCol_TableBorderStrong: return \"TableBorderStrong\";\n    case ImGuiCol_TableBorderLight: return \"TableBorderLight\";\n    case ImGuiCol_TableRowBg: return \"TableRowBg\";\n    case ImGuiCol_TableRowBgAlt: return \"TableRowBgAlt\";\n    case ImGuiCol_TextSelectedBg: return \"TextSelectedBg\";\n    case ImGuiCol_DragDropTarget: return \"DragDropTarget\";\n    case ImGuiCol_NavHighlight: return \"NavHighlight\";\n    case ImGuiCol_NavWindowingHighlight: return \"NavWindowingHighlight\";\n    case ImGuiCol_NavWindowingDimBg: return \"NavWindowingDimBg\";\n    case ImGuiCol_ModalWindowDimBg: return \"ModalWindowDimBg\";\n    }\n    IM_ASSERT(0);\n    return \"Unknown\";\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] RENDER HELPERS\n// Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change,\n// we need a nicer separation between low-level functions and high-level functions relying on the ImGui context.\n// Also see imgui_draw.cpp for some more which have been reworked to not rely on ImGui:: context.\n//-----------------------------------------------------------------------------\n\nconst char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end)\n{\n    const char* text_display_end = text;\n    if (!text_end)\n        text_end = (const char*)-1;\n\n    while (text_display_end < text_end && *text_display_end != '\\0' && (text_display_end[0] != '#' || text_display_end[1] != '#'))\n        text_display_end++;\n    return text_display_end;\n}\n\n// Internal ImGui functions to render text\n// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText()\nvoid ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Hide anything after a '##' string\n    const char* text_display_end;\n    if (hide_text_after_hash)\n    {\n        text_display_end = FindRenderedTextEnd(text, text_end);\n    }\n    else\n    {\n        if (!text_end)\n            text_end = text + strlen(text); // FIXME-OPT\n        text_display_end = text_end;\n    }\n\n    if (text != text_display_end)\n    {\n        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);\n        if (g.LogEnabled)\n            LogRenderedText(&pos, text, text_display_end);\n    }\n}\n\nvoid ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    if (!text_end)\n        text_end = text + strlen(text); // FIXME-OPT\n\n    if (text != text_end)\n    {\n        window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);\n        if (g.LogEnabled)\n            LogRenderedText(&pos, text, text_end);\n    }\n}\n\n// Default clip_rect uses (pos_min,pos_max)\n// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges)\n// FIXME-OPT: Since we have or calculate text_size we could coarse clip whole block immediately, especally for text above draw_list->DrawList.\n// Effectively as this is called from widget doing their own coarse clipping it's not very valuable presently. Next time function will take\n// better advantage of the render function taking size into account for coarse clipping.\nvoid ImGui::RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_display_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)\n{\n    // Perform CPU side clipping for single clipped element to avoid using scissor state\n    ImVec2 pos = pos_min;\n    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f);\n\n    const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min;\n    const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max;\n    bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y);\n    if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min\n        need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y);\n\n    // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment.\n    if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x);\n    if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y);\n\n    // Render\n    if (need_clipping)\n    {\n        ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y);\n        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect);\n    }\n    else\n    {\n        draw_list->AddText(NULL, 0.0f, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL);\n    }\n}\n\nvoid ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect)\n{\n    // Hide anything after a '##' string\n    const char* text_display_end = FindRenderedTextEnd(text, text_end);\n    const int text_len = (int)(text_display_end - text);\n    if (text_len == 0)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    RenderTextClippedEx(window->DrawList, pos_min, pos_max, text, text_display_end, text_size_if_known, align, clip_rect);\n    if (g.LogEnabled)\n        LogRenderedText(&pos_min, text, text_display_end);\n}\n\n// Another overly complex function until we reorganize everything into a nice all-in-one helper.\n// This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display.\n// This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.\nvoid ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)\n{\n    ImGuiContext& g = *GImGui;\n    if (text_end_full == NULL)\n        text_end_full = FindRenderedTextEnd(text);\n    const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);\n\n    //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 4), IM_COL32(0, 0, 255, 255));\n    //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y-2), ImVec2(ellipsis_max_x, pos_max.y+2), IM_COL32(0, 255, 0, 255));\n    //draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));\n    // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.\n    if (text_size.x > pos_max.x - pos_min.x)\n    {\n        // Hello wo...\n        // |       |   |\n        // min   max   ellipsis_max\n        //          <-> this is generally some padding value\n\n        const ImFont* font = draw_list->_Data->Font;\n        const float font_size = draw_list->_Data->FontSize;\n        const float font_scale = font_size / font->FontSize;\n        const char* text_end_ellipsis = NULL;\n        const float ellipsis_width = font->EllipsisWidth * font_scale;\n\n        // We can now claim the space between pos_max.x and ellipsis_max.x\n        const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_max_x) - ellipsis_width) - pos_min.x, 1.0f);\n        float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;\n        if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)\n        {\n            // Always display at least 1 character if there's no room for character + ellipsis\n            text_end_ellipsis = text + ImTextCountUtf8BytesFromChar(text, text_end_full);\n            text_size_clipped_x = font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text, text_end_ellipsis).x;\n        }\n        while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))\n        {\n            // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text)\n            text_end_ellipsis--;\n            text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte\n        }\n\n        // Render text, render ellipsis\n        RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));\n        ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));\n        if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x)\n            for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale)\n                font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar);\n    }\n    else\n    {\n        RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));\n    }\n\n    if (g.LogEnabled)\n        LogRenderedText(&pos_min, text, text_end_full);\n}\n\n// Render a rectangle shaped with optional rounding and borders\nvoid ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding);\n    const float border_size = g.Style.FrameBorderSize;\n    if (border && border_size > 0.0f)\n    {\n        window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);\n        window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);\n    }\n}\n\nvoid ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    const float border_size = g.Style.FrameBorderSize;\n    if (border_size > 0.0f)\n    {\n        window->DrawList->AddRect(p_min + ImVec2(1, 1), p_max + ImVec2(1, 1), GetColorU32(ImGuiCol_BorderShadow), rounding, 0, border_size);\n        window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);\n    }\n}\n\nvoid ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (id != g.NavId)\n        return;\n    if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw))\n        return;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->DC.NavHideHighlightOneFrame)\n        return;\n\n    float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding;\n    ImRect display_rect = bb;\n    display_rect.ClipWith(window->ClipRect);\n    if (flags & ImGuiNavHighlightFlags_TypeDefault)\n    {\n        const float THICKNESS = 2.0f;\n        const float DISTANCE = 3.0f + THICKNESS * 0.5f;\n        display_rect.Expand(ImVec2(DISTANCE, DISTANCE));\n        bool fully_visible = window->ClipRect.Contains(display_rect);\n        if (!fully_visible)\n            window->DrawList->PushClipRect(display_rect.Min, display_rect.Max);\n        window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), display_rect.Max - ImVec2(THICKNESS * 0.5f, THICKNESS * 0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, 0, THICKNESS);\n        if (!fully_visible)\n            window->DrawList->PopClipRect();\n    }\n    if (flags & ImGuiNavHighlightFlags_TypeThin)\n    {\n        window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, 0, 1.0f);\n    }\n}\n\nvoid ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT);\n    ImFontAtlas* font_atlas = g.DrawListSharedData.Font->ContainerAtlas;\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        // We scale cursor with current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor.\n        ImVec2 offset, size, uv[4];\n        if (!font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2]))\n            continue;\n        const ImVec2 pos = base_pos - offset;\n        const float scale = base_scale;\n        if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale)))\n            continue;\n        ImDrawList* draw_list = GetForegroundDrawList(viewport);\n        ImTextureID tex_id = font_atlas->TexID;\n        draw_list->PushTextureID(tex_id);\n        draw_list->AddImage(tex_id, pos + ImVec2(1, 0) * scale, pos + (ImVec2(1, 0) + size) * scale, uv[2], uv[3], col_shadow);\n        draw_list->AddImage(tex_id, pos + ImVec2(2, 0) * scale, pos + (ImVec2(2, 0) + size) * scale, uv[2], uv[3], col_shadow);\n        draw_list->AddImage(tex_id, pos,                        pos + size * scale,                  uv[2], uv[3], col_border);\n        draw_list->AddImage(tex_id, pos,                        pos + size * scale,                  uv[0], uv[1], col_fill);\n        draw_list->PopTextureID();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] INITIALIZATION, SHUTDOWN\n//-----------------------------------------------------------------------------\n\n// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself\n// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module\nImGuiContext* ImGui::GetCurrentContext()\n{\n    return GImGui;\n}\n\nvoid ImGui::SetCurrentContext(ImGuiContext* ctx)\n{\n#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC\n    IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this.\n#else\n    GImGui = ctx;\n#endif\n}\n\nvoid ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data)\n{\n    GImAllocatorAllocFunc = alloc_func;\n    GImAllocatorFreeFunc = free_func;\n    GImAllocatorUserData = user_data;\n}\n\n// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space)\nvoid ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data)\n{\n    *p_alloc_func = GImAllocatorAllocFunc;\n    *p_free_func = GImAllocatorFreeFunc;\n    *p_user_data = GImAllocatorUserData;\n}\n\nImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas)\n{\n    ImGuiContext* prev_ctx = GetCurrentContext();\n    ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas);\n    SetCurrentContext(ctx);\n    Initialize();\n    if (prev_ctx != NULL)\n        SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one.\n    return ctx;\n}\n\nvoid ImGui::DestroyContext(ImGuiContext* ctx)\n{\n    ImGuiContext* prev_ctx = GetCurrentContext();\n    if (ctx == NULL) //-V1051\n        ctx = prev_ctx;\n    SetCurrentContext(ctx);\n    Shutdown();\n    SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL);\n    IM_DELETE(ctx);\n}\n\n// IMPORTANT: ###xxx suffixes must be same in ALL languages\nstatic const ImGuiLocEntry GLocalizationEntriesEnUS[] =\n{\n    { ImGuiLocKey_VersionStr,           \"Dear ImGui \" IMGUI_VERSION \" (\" IM_STRINGIFY(IMGUI_VERSION_NUM) \")\" },\n    { ImGuiLocKey_TableSizeOne,         \"Size column to fit###SizeOne\"          },\n    { ImGuiLocKey_TableSizeAllFit,      \"Size all columns to fit###SizeAll\"     },\n    { ImGuiLocKey_TableSizeAllDefault,  \"Size all columns to default###SizeAll\" },\n    { ImGuiLocKey_TableResetOrder,      \"Reset order###ResetOrder\"              },\n    { ImGuiLocKey_WindowingMainMenuBar, \"(Main menu bar)\"                       },\n    { ImGuiLocKey_WindowingPopup,       \"(Popup)\"                               },\n    { ImGuiLocKey_WindowingUntitled,    \"(Untitled)\"                            },\n};\n\nvoid ImGui::Initialize()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(!g.Initialized && !g.SettingsLoaded);\n\n    // Add .ini handle for ImGuiWindow and ImGuiTable types\n    {\n        ImGuiSettingsHandler ini_handler;\n        ini_handler.TypeName = \"Window\";\n        ini_handler.TypeHash = ImHashStr(\"Window\");\n        ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll;\n        ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen;\n        ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine;\n        ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll;\n        ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll;\n        AddSettingsHandler(&ini_handler);\n    }\n    TableSettingsAddSettingsHandler();\n\n    // Setup default localization table\n    LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS));\n\n    // Setup default platform clipboard/IME handlers.\n    g.IO.GetClipboardTextFn = GetClipboardTextFn_DefaultImpl;    // Platform dependent default implementations\n    g.IO.SetClipboardTextFn = SetClipboardTextFn_DefaultImpl;\n    g.IO.ClipboardUserData = (void*)&g;                          // Default implementation use the ImGuiContext as user data (ideally those would be arguments to the function)\n    g.IO.SetPlatformImeDataFn = SetPlatformImeDataFn_DefaultImpl;\n\n    // Create default viewport\n    ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)();\n    g.Viewports.push_back(viewport);\n    g.TempBuffer.resize(1024 * 3 + 1, 0);\n\n#ifdef IMGUI_HAS_DOCK\n#endif\n\n    g.Initialized = true;\n}\n\n// This function is merely here to free heap allocations.\nvoid ImGui::Shutdown()\n{\n    // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame)\n    ImGuiContext& g = *GImGui;\n    if (g.IO.Fonts && g.FontAtlasOwnedByContext)\n    {\n        g.IO.Fonts->Locked = false;\n        IM_DELETE(g.IO.Fonts);\n    }\n    g.IO.Fonts = NULL;\n    g.DrawListSharedData.TempBuffer.clear();\n\n    // Cleanup of other data are conditional on actually having initialized Dear ImGui.\n    if (!g.Initialized)\n        return;\n\n    // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file)\n    if (g.SettingsLoaded && g.IO.IniFilename != NULL)\n        SaveIniSettingsToDisk(g.IO.IniFilename);\n\n    CallContextHooks(&g, ImGuiContextHookType_Shutdown);\n\n    // Clear everything else\n    g.Windows.clear_delete();\n    g.WindowsFocusOrder.clear();\n    g.WindowsTempSortBuffer.clear();\n    g.CurrentWindow = NULL;\n    g.CurrentWindowStack.clear();\n    g.WindowsById.Clear();\n    g.NavWindow = NULL;\n    g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;\n    g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;\n    g.MovingWindow = NULL;\n\n    g.KeysRoutingTable.Clear();\n\n    g.ColorStack.clear();\n    g.StyleVarStack.clear();\n    g.FontStack.clear();\n    g.OpenPopupStack.clear();\n    g.BeginPopupStack.clear();\n    g.NavTreeNodeStack.clear();\n\n    g.Viewports.clear_delete();\n\n    g.TabBars.Clear();\n    g.CurrentTabBarStack.clear();\n    g.ShrinkWidthBuffer.clear();\n\n    g.ClipperTempData.clear_destruct();\n\n    g.Tables.Clear();\n    g.TablesTempData.clear_destruct();\n    g.DrawChannelsTempMergeBuffer.clear();\n\n    g.ClipboardHandlerData.clear();\n    g.MenusIdSubmittedThisFrame.clear();\n    g.InputTextState.ClearFreeMemory();\n    g.InputTextDeactivatedState.ClearFreeMemory();\n\n    g.SettingsWindows.clear();\n    g.SettingsHandlers.clear();\n\n    if (g.LogFile)\n    {\n#ifndef IMGUI_DISABLE_TTY_FUNCTIONS\n        if (g.LogFile != stdout)\n#endif\n            ImFileClose(g.LogFile);\n        g.LogFile = NULL;\n    }\n    g.LogBuffer.clear();\n    g.DebugLogBuf.clear();\n    g.DebugLogIndex.clear();\n\n    g.Initialized = false;\n}\n\n// No specific ordering/dependency support, will see as needed\nImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook)\n{\n    ImGuiContext& g = *ctx;\n    IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_);\n    g.Hooks.push_back(*hook);\n    g.Hooks.back().HookId = ++g.HookIdNext;\n    return g.HookIdNext;\n}\n\n// Deferred removal, avoiding issue with changing vector while iterating it\nvoid ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id)\n{\n    ImGuiContext& g = *ctx;\n    IM_ASSERT(hook_id != 0);\n    for (ImGuiContextHook& hook : g.Hooks)\n        if (hook.HookId == hook_id)\n            hook.Type = ImGuiContextHookType_PendingRemoval_;\n}\n\n// Call context hooks (used by e.g. test engine)\n// We assume a small number of hooks so all stored in same array\nvoid ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type)\n{\n    ImGuiContext& g = *ctx;\n    for (ImGuiContextHook& hook : g.Hooks)\n        if (hook.Type == hook_type)\n            hook.Callback(&g, &hook);\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!)\n//-----------------------------------------------------------------------------\n\n// ImGuiWindow is mostly a dumb struct. It merely has a constructor and a few helper methods\nImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NULL)\n{\n    memset(this, 0, sizeof(*this));\n    Ctx = ctx;\n    Name = ImStrdup(name);\n    NameBufLen = (int)strlen(name) + 1;\n    ID = ImHashStr(name);\n    IDStack.push_back(ID);\n    MoveId = GetID(\"#MOVE\");\n    ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);\n    ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f);\n    AutoFitFramesX = AutoFitFramesY = -1;\n    AutoPosLastDirection = ImGuiDir_None;\n    SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = 0;\n    SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);\n    LastFrameActive = -1;\n    LastTimeActive = -1.0f;\n    FontWindowScale = 1.0f;\n    SettingsOffset = -1;\n    DrawList = &DrawListInst;\n    DrawList->_Data = &Ctx->DrawListSharedData;\n    DrawList->_OwnerName = Name;\n    NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX);\n}\n\nImGuiWindow::~ImGuiWindow()\n{\n    IM_ASSERT(DrawList == &DrawListInst);\n    IM_DELETE(Name);\n    ColumnsStorage.clear_destruct();\n}\n\nImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)\n{\n    ImGuiID seed = IDStack.back();\n    ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);\n    ImGuiContext& g = *Ctx;\n    if (g.DebugHookIdInfo == id)\n        ImGui::DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);\n    return id;\n}\n\nImGuiID ImGuiWindow::GetID(const void* ptr)\n{\n    ImGuiID seed = IDStack.back();\n    ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);\n    ImGuiContext& g = *Ctx;\n    if (g.DebugHookIdInfo == id)\n        ImGui::DebugHookIdInfo(id, ImGuiDataType_Pointer, ptr, NULL);\n    return id;\n}\n\nImGuiID ImGuiWindow::GetID(int n)\n{\n    ImGuiID seed = IDStack.back();\n    ImGuiID id = ImHashData(&n, sizeof(n), seed);\n    ImGuiContext& g = *Ctx;\n    if (g.DebugHookIdInfo == id)\n        ImGui::DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);\n    return id;\n}\n\n// This is only used in rare/specific situations to manufacture an ID out of nowhere.\nImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)\n{\n    ImGuiID seed = IDStack.back();\n    ImRect r_rel = ImGui::WindowRectAbsToRel(this, r_abs);\n    ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);\n    return id;\n}\n\nstatic void SetCurrentWindow(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    g.CurrentWindow = window;\n    g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL;\n    if (window)\n    {\n        g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();\n        ImGui::NavUpdateCurrentWindowIsScrollPushableX();\n    }\n}\n\nvoid ImGui::GcCompactTransientMiscBuffers()\n{\n    ImGuiContext& g = *GImGui;\n    g.ItemFlagsStack.clear();\n    g.GroupStack.clear();\n    TableGcCompactSettings();\n}\n\n// Free up/compact internal window buffers, we can use this when a window becomes unused.\n// Not freed:\n// - ImGuiWindow, ImGuiWindowSettings, Name, StateStorage, ColumnsStorage (may hold useful data)\n// This should have no noticeable visual effect. When the window reappear however, expect new allocation/buffer growth/copy cost.\nvoid ImGui::GcCompactTransientWindowBuffers(ImGuiWindow* window)\n{\n    window->MemoryCompacted = true;\n    window->MemoryDrawListIdxCapacity = window->DrawList->IdxBuffer.Capacity;\n    window->MemoryDrawListVtxCapacity = window->DrawList->VtxBuffer.Capacity;\n    window->IDStack.clear();\n    window->DrawList->_ClearFreeMemory();\n    window->DC.ChildWindows.clear();\n    window->DC.ItemWidthStack.clear();\n    window->DC.TextWrapPosStack.clear();\n}\n\nvoid ImGui::GcAwakeTransientWindowBuffers(ImGuiWindow* window)\n{\n    // We stored capacity of the ImDrawList buffer to reduce growth-caused allocation/copy when awakening.\n    // The other buffers tends to amortize much faster.\n    window->MemoryCompacted = false;\n    window->DrawList->IdxBuffer.reserve(window->MemoryDrawListIdxCapacity);\n    window->DrawList->VtxBuffer.reserve(window->MemoryDrawListVtxCapacity);\n    window->MemoryDrawListIdxCapacity = window->MemoryDrawListVtxCapacity = 0;\n}\n\nvoid ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Clear previous active id\n    if (g.ActiveId != 0)\n    {\n        // While most behaved code would make an effort to not steal active id during window move/drag operations,\n        // we at least need to be resilient to it. Canceling the move is rather aggressive and users of 'master' branch\n        // may prefer the weird ill-defined half working situation ('docking' did assert), so may need to rework that.\n        if (g.MovingWindow != NULL && g.ActiveId == g.MovingWindow->MoveId)\n        {\n            IMGUI_DEBUG_LOG_ACTIVEID(\"SetActiveID() cancel MovingWindow\\n\");\n            g.MovingWindow = NULL;\n        }\n\n        // This could be written in a more general way (e.g associate a hook to ActiveId),\n        // but since this is currently quite an exception we'll leave it as is.\n        // One common scenario leading to this is: pressing Key ->NavMoveRequestApplyResult() -> ClearActiveId()\n        if (g.InputTextState.ID == g.ActiveId)\n            InputTextDeactivateHook(g.ActiveId);\n    }\n\n    // Set active id\n    g.ActiveIdIsJustActivated = (g.ActiveId != id);\n    if (g.ActiveIdIsJustActivated)\n    {\n        IMGUI_DEBUG_LOG_ACTIVEID(\"SetActiveID() old:0x%08X (window \\\"%s\\\") -> new:0x%08X (window \\\"%s\\\")\\n\", g.ActiveId, g.ActiveIdWindow ? g.ActiveIdWindow->Name : \"\", id, window ? window->Name : \"\");\n        g.ActiveIdTimer = 0.0f;\n        g.ActiveIdHasBeenPressedBefore = false;\n        g.ActiveIdHasBeenEditedBefore = false;\n        g.ActiveIdMouseButton = -1;\n        if (id != 0)\n        {\n            g.LastActiveId = id;\n            g.LastActiveIdTimer = 0.0f;\n        }\n    }\n    g.ActiveId = id;\n    g.ActiveIdAllowOverlap = false;\n    g.ActiveIdNoClearOnFocusLoss = false;\n    g.ActiveIdWindow = window;\n    g.ActiveIdHasBeenEditedThisFrame = false;\n    if (id)\n    {\n        g.ActiveIdIsAlive = id;\n        g.ActiveIdSource = (g.NavActivateId == id || g.NavJustMovedToId == id) ? g.NavInputSource : ImGuiInputSource_Mouse;\n        IM_ASSERT(g.ActiveIdSource != ImGuiInputSource_None);\n    }\n\n    // Clear declaration of inputs claimed by the widget\n    // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet)\n    g.ActiveIdUsingNavDirMask = 0x00;\n    g.ActiveIdUsingAllKeyboardKeys = false;\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    g.ActiveIdUsingNavInputMask = 0x00;\n#endif\n}\n\nvoid ImGui::ClearActiveID()\n{\n    SetActiveID(0, NULL); // g.ActiveId = 0;\n}\n\nvoid ImGui::SetHoveredID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    g.HoveredId = id;\n    g.HoveredIdAllowOverlap = false;\n    if (id != 0 && g.HoveredIdPreviousFrame != id)\n        g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f;\n}\n\nImGuiID ImGui::GetHoveredID()\n{\n    ImGuiContext& g = *GImGui;\n    return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame;\n}\n\n// This is called by ItemAdd().\n// Code not using ItemAdd() may need to call this manually otherwise ActiveId will be cleared. In IMGUI_VERSION_NUM < 18717 this was called by GetID().\nvoid ImGui::KeepAliveID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveId == id)\n        g.ActiveIdIsAlive = id;\n    if (g.ActiveIdPreviousFrame == id)\n        g.ActiveIdPreviousFrameIsAlive = true;\n}\n\nvoid ImGui::MarkItemEdited(ImGuiID id)\n{\n    // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit().\n    // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data.\n    ImGuiContext& g = *GImGui;\n    if (g.LockMarkEdited > 0)\n        return;\n    if (g.ActiveId == id || g.ActiveId == 0)\n    {\n        g.ActiveIdHasBeenEditedThisFrame = true;\n        g.ActiveIdHasBeenEditedBefore = true;\n    }\n\n    // We accept a MarkItemEdited() on drag and drop targets (see https://github.com/ocornut/imgui/issues/1875#issuecomment-978243343)\n    // We accept 'ActiveIdPreviousFrame == id' for InputText() returning an edit after it has been taken ActiveId away (#4714)\n    IM_ASSERT(g.DragDropActive || g.ActiveId == id || g.ActiveId == 0 || g.ActiveIdPreviousFrame == id);\n\n    //IM_ASSERT(g.CurrentWindow->DC.LastItemId == id);\n    g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;\n}\n\nbool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)\n{\n    // An active popup disable hovering on other windows (apart from its own children)\n    // FIXME-OPT: This could be cached/stored within the window.\n    ImGuiContext& g = *GImGui;\n    if (g.NavWindow)\n        if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)\n            if (focused_root_window->WasActive && focused_root_window != window->RootWindow)\n            {\n                // For the purpose of those flags we differentiate \"standard popup\" from \"modal popup\"\n                // NB: The 'else' is important because Modal windows are also Popups.\n                bool want_inhibit = false;\n                if (focused_root_window->Flags & ImGuiWindowFlags_Modal)\n                    want_inhibit = true;\n                else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))\n                    want_inhibit = true;\n\n                // Inhibit hover unless the window is within the stack of our modal/popup\n                if (want_inhibit)\n                    if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window))\n                        return false;\n            }\n    return true;\n}\n\nstatic inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (flags & ImGuiHoveredFlags_DelayNormal)\n        return g.Style.HoverDelayNormal;\n    if (flags & ImGuiHoveredFlags_DelayShort)\n        return g.Style.HoverDelayShort;\n    return 0.0f;\n}\n\nstatic ImGuiHoveredFlags ApplyHoverFlagsForTooltip(ImGuiHoveredFlags user_flags, ImGuiHoveredFlags shared_flags)\n{\n    // Allow instance flags to override shared flags\n    if (user_flags & (ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal))\n        shared_flags &= ~(ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal);\n    return user_flags | shared_flags;\n}\n\n// This is roughly matching the behavior of internal-facing ItemHoverable()\n// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()\n// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId\nbool ImGui::IsItemHovered(ImGuiHoveredFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0 && \"Invalid flags for IsItemHovered()!\");\n\n    if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride))\n    {\n        if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))\n            return false;\n        if (!IsItemFocused())\n            return false;\n\n        if (flags & ImGuiHoveredFlags_ForTooltip)\n            flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipNav);\n    }\n    else\n    {\n        // Test for bounding box overlap, as updated as ItemAdd()\n        ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;\n        if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))\n            return false;\n\n        if (flags & ImGuiHoveredFlags_ForTooltip)\n            flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);\n\n        IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0);   // Flags not supported by this function\n\n        // Done with rectangle culling so we can perform heavier checks now\n        // Test if we are hovering the right window (our window could be behind another window)\n        // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851)\n        // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable\n        // to use IsItemHovered() after EndChild() itself. Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was\n        // the test that has been running for a long while.\n        if (g.HoveredWindow != window && (status_flags & ImGuiItemStatusFlags_HoveredWindow) == 0)\n            if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByWindow) == 0)\n                return false;\n\n        // Test if another item is active (e.g. being dragged)\n        const ImGuiID id = g.LastItemData.ID;\n        if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0)\n            if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)\n                return false;\n\n        // Test if interactions on this window are blocked by an active popup or modal.\n        // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here.\n        if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.InFlags & ImGuiItemFlags_NoWindowHoverableCheck))\n            return false;\n\n        // Test if the item is disabled\n        if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))\n            return false;\n\n        // Special handling for calling after Begin() which represent the title bar or tab.\n        // When the window is skipped/collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.\n        if (id == window->MoveId && window->WriteAccessed)\n            return false;\n\n        // Test if using AllowOverlap and overlapped\n        if ((g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap) && id != 0)\n            if ((flags & ImGuiHoveredFlags_AllowWhenOverlappedByItem) == 0)\n                if (g.HoveredIdPreviousFrame != g.LastItemData.ID)\n                    return false;\n    }\n\n    // Handle hover delay\n    // (some ideas: https://www.nngroup.com/articles/timing-exposing-content)\n    const float delay = CalcDelayFromHoveredFlags(flags);\n    if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))\n    {\n        ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect);\n        if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))\n            g.HoverItemDelayTimer = 0.0f;\n        g.HoverItemDelayId = hover_delay_id;\n\n        // When changing hovered item we requires a bit of stationary delay before activating hover timer,\n        // but once unlocked on a given item we also moving.\n        //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG(\"HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\\n\", g.HoverDelayTimer, delay, g.MouseStationaryTimer); }\n        if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)\n            return false;\n\n        if (g.HoverItemDelayTimer < delay)\n            return false;\n    }\n\n    return true;\n}\n\n// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().\n// (this does not rely on LastItemData it can be called from a ButtonBehavior() call not following an ItemAdd() call)\n// FIXME-LEGACY: the 'ImGuiItemFlags item_flags' parameter was added on 2023-06-28.\n// If you used this in your legacy/custom widgets code:\n// - Commonly: if your ItemHoverable() call comes after an ItemAdd() call: pass 'item_flags = g.LastItemData.InFlags'.\n// - Rare: otherwise you may pass 'item_flags = 0' (ImGuiItemFlags_None) unless you want to benefit from special behavior handled by ItemHoverable.\nbool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (g.HoveredWindow != window)\n        return false;\n    if (!IsMouseHoveringRect(bb.Min, bb.Max))\n        return false;\n\n    if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap)\n        return false;\n    if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap)\n        return false;\n\n    // Done with rectangle culling so we can perform heavier checks now.\n    if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None))\n    {\n        g.HoveredIdDisabled = true;\n        return false;\n    }\n\n    // We exceptionally allow this function to be called with id==0 to allow using it for easy high-level\n    // hover test in widgets code. We could also decide to split this function is two.\n    if (id != 0)\n    {\n        // Drag source doesn't report as hovered\n        if (g.DragDropActive && g.DragDropPayload.SourceId == id && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoDisableHover))\n            return false;\n\n        SetHoveredID(id);\n\n        // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match.\n        // This allows using patterns where a later submitted widget overlaps a previous one. Generally perceived as a front-to-back hit-test.\n        if (item_flags & ImGuiItemFlags_AllowOverlap)\n        {\n            g.HoveredIdAllowOverlap = true;\n            if (g.HoveredIdPreviousFrame != id)\n                return false;\n        }\n    }\n\n    // When disabled we'll return false but still set HoveredId\n    if (item_flags & ImGuiItemFlags_Disabled)\n    {\n        // Release active id if turning disabled\n        if (g.ActiveId == id && id != 0)\n            ClearActiveID();\n        g.HoveredIdDisabled = true;\n        return false;\n    }\n\n    if (id != 0)\n    {\n        // [DEBUG] Item Picker tool!\n        // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making\n        // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered\n        // items if we performed the test in ItemAdd(), but that would incur a small runtime cost.\n        if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id)\n            GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255));\n        if (g.DebugItemPickerBreakId == id)\n            IM_DEBUG_BREAK();\n    }\n\n    if (g.NavDisableMouseHover)\n        return false;\n\n    return true;\n}\n\n// FIXME: This is inlined/duplicated in ItemAdd()\nbool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (!bb.Overlaps(window->ClipRect))\n        if (id == 0 || (id != g.ActiveId && id != g.NavId))\n            if (!g.LogEnabled)\n                return true;\n    return false;\n}\n\n// This is also inlined in ItemAdd()\n// Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect.\nvoid ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect)\n{\n    ImGuiContext& g = *GImGui;\n    g.LastItemData.ID = item_id;\n    g.LastItemData.InFlags = in_flags;\n    g.LastItemData.StatusFlags = item_flags;\n    g.LastItemData.Rect = g.LastItemData.NavRect = item_rect;\n}\n\nfloat ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x)\n{\n    if (wrap_pos_x < 0.0f)\n        return 0.0f;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (wrap_pos_x == 0.0f)\n    {\n        // We could decide to setup a default wrapping max point for auto-resizing windows,\n        // or have auto-wrap (with unspecified wrapping pos) behave as a ContentSize extending function?\n        //if (window->Hidden && (window->Flags & ImGuiWindowFlags_AlwaysAutoResize))\n        //    wrap_pos_x = ImMax(window->WorkRect.Min.x + g.FontSize * 10.0f, window->WorkRect.Max.x);\n        //else\n        wrap_pos_x = window->WorkRect.Max.x;\n    }\n    else if (wrap_pos_x > 0.0f)\n    {\n        wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space\n    }\n\n    return ImMax(wrap_pos_x - pos.x, 1.0f);\n}\n\n// IM_ALLOC() == ImGui::MemAlloc()\nvoid* ImGui::MemAlloc(size_t size)\n{\n    void* ptr = (*GImAllocatorAllocFunc)(size, GImAllocatorUserData);\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (ImGuiContext* ctx = GImGui)\n        DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, size);\n#endif\n    return ptr;\n}\n\n// IM_FREE() == ImGui::MemFree()\nvoid ImGui::MemFree(void* ptr)\n{\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (ptr != NULL)\n        if (ImGuiContext* ctx = GImGui)\n            DebugAllocHook(&ctx->DebugAllocInfo, ctx->FrameCount, ptr, (size_t)-1);\n#endif\n    return (*GImAllocatorFreeFunc)(ptr, GImAllocatorUserData);\n}\n\n// We record the number of allocation in recent frames, as a way to audit/sanitize our guiding principles of \"no allocations on idle/repeating frames\"\nvoid ImGui::DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size)\n{\n    ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[info->LastEntriesIdx];\n    IM_UNUSED(ptr);\n    if (entry->FrameCount != frame_count)\n    {\n        info->LastEntriesIdx = (info->LastEntriesIdx + 1) % IM_ARRAYSIZE(info->LastEntriesBuf);\n        entry = &info->LastEntriesBuf[info->LastEntriesIdx];\n        entry->FrameCount = frame_count;\n        entry->AllocCount = entry->FreeCount = 0;\n    }\n    if (size != (size_t)-1)\n    {\n        entry->AllocCount++;\n        info->TotalAllocCount++;\n        //printf(\"[%05d] MemAlloc(%d) -> 0x%p\\n\", frame_count, size, ptr);\n    }\n    else\n    {\n        entry->FreeCount++;\n        info->TotalFreeCount++;\n        //printf(\"[%05d] MemFree(0x%p)\\n\", frame_count, ptr);\n    }\n}\n\nconst char* ImGui::GetClipboardText()\n{\n    ImGuiContext& g = *GImGui;\n    return g.IO.GetClipboardTextFn ? g.IO.GetClipboardTextFn(g.IO.ClipboardUserData) : \"\";\n}\n\nvoid ImGui::SetClipboardText(const char* text)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.IO.SetClipboardTextFn)\n        g.IO.SetClipboardTextFn(g.IO.ClipboardUserData, text);\n}\n\nconst char* ImGui::GetVersion()\n{\n    return IMGUI_VERSION;\n}\n\nImGuiIO& ImGui::GetIO()\n{\n    IM_ASSERT(GImGui != NULL && \"No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?\");\n    return GImGui->IO;\n}\n\n// Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame()\nImDrawData* ImGui::GetDrawData()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiViewportP* viewport = g.Viewports[0];\n    return viewport->DrawDataP.Valid ? &viewport->DrawDataP : NULL;\n}\n\ndouble ImGui::GetTime()\n{\n    return GImGui->Time;\n}\n\nint ImGui::GetFrameCount()\n{\n    return GImGui->FrameCount;\n}\n\nstatic ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name)\n{\n    // Create the draw list on demand, because they are not frequently used for all viewports\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->BgFgDrawLists));\n    ImDrawList* draw_list = viewport->BgFgDrawLists[drawlist_no];\n    if (draw_list == NULL)\n    {\n        draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData);\n        draw_list->_OwnerName = drawlist_name;\n        viewport->BgFgDrawLists[drawlist_no] = draw_list;\n    }\n\n    // Our ImDrawList system requires that there is always a command\n    if (viewport->BgFgDrawListsLastFrame[drawlist_no] != g.FrameCount)\n    {\n        draw_list->_ResetForNewFrame();\n        draw_list->PushTextureID(g.IO.Fonts->TexID);\n        draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false);\n        viewport->BgFgDrawListsLastFrame[drawlist_no] = g.FrameCount;\n    }\n    return draw_list;\n}\n\nImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport)\n{\n    return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, \"##Background\");\n}\n\nImDrawList* ImGui::GetBackgroundDrawList()\n{\n    ImGuiContext& g = *GImGui;\n    return GetBackgroundDrawList(g.Viewports[0]);\n}\n\nImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport)\n{\n    return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, \"##Foreground\");\n}\n\nImDrawList* ImGui::GetForegroundDrawList()\n{\n    ImGuiContext& g = *GImGui;\n    return GetForegroundDrawList(g.Viewports[0]);\n}\n\nImDrawListSharedData* ImGui::GetDrawListSharedData()\n{\n    return &GImGui->DrawListSharedData;\n}\n\nvoid ImGui::StartMouseMovingWindow(ImGuiWindow* window)\n{\n    // Set ActiveId even if the _NoMove flag is set. Without it, dragging away from a window with _NoMove would activate hover on other windows.\n    // We _also_ call this when clicking in a window empty space when io.ConfigWindowsMoveFromTitleBarOnly is set, but clear g.MovingWindow afterward.\n    // This is because we want ActiveId to be set even when the window is not permitted to move.\n    ImGuiContext& g = *GImGui;\n    FocusWindow(window);\n    SetActiveID(window->MoveId, window);\n    g.NavDisableHighlight = true;\n    g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos;\n    g.ActiveIdNoClearOnFocusLoss = true;\n    SetActiveIdUsingAllKeyboardKeys();\n\n    bool can_move_window = true;\n    if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove))\n        can_move_window = false;\n    if (can_move_window)\n        g.MovingWindow = window;\n}\n\n// Handle mouse moving window\n// Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing()\n// FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId.\n// This is currently enforced by the fact that BeginDragDropSource() is setting all g.ActiveIdUsingXXXX flags to inhibit navigation inputs,\n// but if we should more thoroughly test cases where g.ActiveId or g.MovingWindow gets changed and not the other.\nvoid ImGui::UpdateMouseMovingWindowNewFrame()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.MovingWindow != NULL)\n    {\n        // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window).\n        // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency.\n        KeepAliveID(g.ActiveId);\n        IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow);\n        ImGuiWindow* moving_window = g.MovingWindow->RootWindow;\n        if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos))\n        {\n            ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset;\n            SetWindowPos(moving_window, pos, ImGuiCond_Always);\n            FocusWindow(g.MovingWindow);\n        }\n        else\n        {\n            g.MovingWindow = NULL;\n            ClearActiveID();\n        }\n    }\n    else\n    {\n        // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others.\n        if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId)\n        {\n            KeepAliveID(g.ActiveId);\n            if (!g.IO.MouseDown[0])\n                ClearActiveID();\n        }\n    }\n}\n\n// Initiate moving window when clicking on empty space or title bar.\n// Handle left-click and right-click focus.\nvoid ImGui::UpdateMouseMovingWindowEndFrame()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveId != 0 || g.HoveredId != 0)\n        return;\n\n    // Unless we just made a window/popup appear\n    if (g.NavWindow && g.NavWindow->Appearing)\n        return;\n\n    // Click on empty space to focus window and start moving\n    // (after we're done with all our widgets)\n    if (g.IO.MouseClicked[0])\n    {\n        // Handle the edge case of a popup being closed while clicking in its empty space.\n        // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more.\n        ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;\n        const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel);\n\n        if (root_window != NULL && !is_closed_popup)\n        {\n            StartMouseMovingWindow(g.HoveredWindow); //-V595\n\n            // Cancel moving if clicked outside of title bar\n            if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar))\n                if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0]))\n                    g.MovingWindow = NULL;\n\n            // Cancel moving if clicked over an item which was disabled or inhibited by popups (note that we know HoveredId == 0 already)\n            if (g.HoveredIdDisabled)\n                g.MovingWindow = NULL;\n        }\n        else if (root_window == NULL && g.NavWindow != NULL)\n        {\n            // Clicking on void disable focus\n            FocusWindow(NULL, ImGuiFocusRequestFlags_UnlessBelowModal);\n        }\n    }\n\n    // With right mouse button we close popups without changing focus based on where the mouse is aimed\n    // Instead, focus will be restored to the window under the bottom-most closed popup.\n    // (The left mouse button path calls FocusWindow on the hovered window, which will lead NewFrame->ClosePopupsOverWindow to trigger)\n    if (g.IO.MouseClicked[1])\n    {\n        // Find the top-most window between HoveredWindow and the top-most Modal Window.\n        // This is where we can trim the popup stack.\n        ImGuiWindow* modal = GetTopMostPopupModal();\n        bool hovered_window_above_modal = g.HoveredWindow && (modal == NULL || IsWindowAbove(g.HoveredWindow, modal));\n        ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal, true);\n    }\n}\n\nstatic bool IsWindowActiveAndVisible(ImGuiWindow* window)\n{\n    return (window->Active) && (!window->Hidden);\n}\n\n// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app)\nvoid ImGui::UpdateHoveredWindowAndCaptureFlags()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n    g.WindowsHoverPadding = ImMax(g.Style.TouchExtraPadding, ImVec2(WINDOWS_HOVER_PADDING, WINDOWS_HOVER_PADDING));\n\n    // Find the window hovered by mouse:\n    // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow.\n    // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame.\n    // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.\n    bool clear_hovered_windows = false;\n    FindHoveredWindow();\n\n    // Modal windows prevents mouse from hovering behind them.\n    ImGuiWindow* modal_window = GetTopMostPopupModal();\n    if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window))\n        clear_hovered_windows = true;\n\n    // Disabled mouse?\n    if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)\n        clear_hovered_windows = true;\n\n    // We track click ownership. When clicked outside of a window the click is owned by the application and\n    // won't report hovering nor request capture even while dragging over our windows afterward.\n    const bool has_open_popup = (g.OpenPopupStack.Size > 0);\n    const bool has_open_modal = (modal_window != NULL);\n    int mouse_earliest_down = -1;\n    bool mouse_any_down = false;\n    for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)\n    {\n        if (io.MouseClicked[i])\n        {\n            io.MouseDownOwned[i] = (g.HoveredWindow != NULL) || has_open_popup;\n            io.MouseDownOwnedUnlessPopupClose[i] = (g.HoveredWindow != NULL) || has_open_modal;\n        }\n        mouse_any_down |= io.MouseDown[i];\n        if (io.MouseDown[i])\n            if (mouse_earliest_down == -1 || io.MouseClickedTime[i] < io.MouseClickedTime[mouse_earliest_down])\n                mouse_earliest_down = i;\n    }\n    const bool mouse_avail = (mouse_earliest_down == -1) || io.MouseDownOwned[mouse_earliest_down];\n    const bool mouse_avail_unless_popup_close = (mouse_earliest_down == -1) || io.MouseDownOwnedUnlessPopupClose[mouse_earliest_down];\n\n    // If mouse was first clicked outside of ImGui bounds we also cancel out hovering.\n    // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)\n    const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;\n    if (!mouse_avail && !mouse_dragging_extern_payload)\n        clear_hovered_windows = true;\n\n    if (clear_hovered_windows)\n        g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL;\n\n    // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to Dear ImGui only, false = dispatch mouse to Dear ImGui + underlying app)\n    // Update io.WantCaptureMouseAllowPopupClose (experimental) to give a chance for app to react to popup closure with a drag\n    if (g.WantCaptureMouseNextFrame != -1)\n    {\n        io.WantCaptureMouse = io.WantCaptureMouseUnlessPopupClose = (g.WantCaptureMouseNextFrame != 0);\n    }\n    else\n    {\n        io.WantCaptureMouse = (mouse_avail && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_popup;\n        io.WantCaptureMouseUnlessPopupClose = (mouse_avail_unless_popup_close && (g.HoveredWindow != NULL || mouse_any_down)) || has_open_modal;\n    }\n\n    // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to Dear ImGui only, false = dispatch keyboard info to Dear ImGui + underlying app)\n    if (g.WantCaptureKeyboardNextFrame != -1)\n        io.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0);\n    else\n        io.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL);\n    if (io.NavActive && (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard))\n        io.WantCaptureKeyboard = true;\n\n    // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible\n    io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false;\n}\n\nvoid ImGui::NewFrame()\n{\n    IM_ASSERT(GImGui != NULL && \"No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?\");\n    ImGuiContext& g = *GImGui;\n\n    // Remove pending delete hooks before frame start.\n    // This deferred removal avoid issues of removal while iterating the hook vector\n    for (int n = g.Hooks.Size - 1; n >= 0; n--)\n        if (g.Hooks[n].Type == ImGuiContextHookType_PendingRemoval_)\n            g.Hooks.erase(&g.Hooks[n]);\n\n    CallContextHooks(&g, ImGuiContextHookType_NewFramePre);\n\n    // Check and assert for various common IO and Configuration mistakes\n    ErrorCheckNewFrameSanityChecks();\n\n    // Load settings on first frame, save settings when modified (after a delay)\n    UpdateSettings();\n\n    g.Time += g.IO.DeltaTime;\n    g.WithinFrameScope = true;\n    g.FrameCount += 1;\n    g.TooltipOverrideCount = 0;\n    g.WindowsActiveCount = 0;\n    g.MenusIdSubmittedThisFrame.resize(0);\n\n    // Calculate frame-rate for the user, as a purely luxurious feature\n    g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx];\n    g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime;\n    g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);\n    g.FramerateSecPerFrameCount = ImMin(g.FramerateSecPerFrameCount + 1, IM_ARRAYSIZE(g.FramerateSecPerFrame));\n    g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)g.FramerateSecPerFrameCount)) : FLT_MAX;\n\n    // Process input queue (trickle as many events as possible), turn events into writes to IO structure\n    g.InputEventsTrail.resize(0);\n    UpdateInputEvents(g.IO.ConfigInputTrickleEventQueue);\n\n    // Update viewports (after processing input queue, so io.MouseHoveredViewport is set)\n    UpdateViewportsNewFrame();\n\n    // Setup current font and draw list shared data\n    g.IO.Fonts->Locked = true;\n    SetCurrentFont(GetDefaultFont());\n    IM_ASSERT(g.Font->IsLoaded());\n    ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);\n    for (ImGuiViewportP* viewport : g.Viewports)\n        virtual_space.Add(viewport->GetMainRect());\n    g.DrawListSharedData.ClipRectFullscreen = virtual_space.ToVec4();\n    g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol;\n    g.DrawListSharedData.SetCircleTessellationMaxError(g.Style.CircleTessellationMaxError);\n    g.DrawListSharedData.InitialFlags = ImDrawListFlags_None;\n    if (g.Style.AntiAliasedLines)\n        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLines;\n    if (g.Style.AntiAliasedLinesUseTex && !(g.Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines))\n        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedLinesUseTex;\n    if (g.Style.AntiAliasedFill)\n        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AntiAliasedFill;\n    if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)\n        g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset;\n\n    // Mark rendering data as invalid to prevent user who may have a handle on it to use it.\n    for (ImGuiViewportP* viewport : g.Viewports)\n        viewport->DrawDataP.Valid = false;\n\n    // Drag and drop keep the source ID alive so even if the source disappear our state is consistent\n    if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId)\n        KeepAliveID(g.DragDropPayload.SourceId);\n\n    // Update HoveredId data\n    if (!g.HoveredIdPreviousFrame)\n        g.HoveredIdTimer = 0.0f;\n    if (!g.HoveredIdPreviousFrame || (g.HoveredId && g.ActiveId == g.HoveredId))\n        g.HoveredIdNotActiveTimer = 0.0f;\n    if (g.HoveredId)\n        g.HoveredIdTimer += g.IO.DeltaTime;\n    if (g.HoveredId && g.ActiveId != g.HoveredId)\n        g.HoveredIdNotActiveTimer += g.IO.DeltaTime;\n    g.HoveredIdPreviousFrame = g.HoveredId;\n    g.HoveredId = 0;\n    g.HoveredIdAllowOverlap = false;\n    g.HoveredIdDisabled = false;\n\n    // Clear ActiveID if the item is not alive anymore.\n    // In 1.87, the common most call to KeepAliveID() was moved from GetID() to ItemAdd().\n    // As a result, custom widget using ButtonBehavior() _without_ ItemAdd() need to call KeepAliveID() themselves.\n    if (g.ActiveId != 0 && g.ActiveIdIsAlive != g.ActiveId && g.ActiveIdPreviousFrame == g.ActiveId)\n    {\n        IMGUI_DEBUG_LOG_ACTIVEID(\"NewFrame(): ClearActiveID() because it isn't marked alive anymore!\\n\");\n        ClearActiveID();\n    }\n\n    // Update ActiveId data (clear reference to active widget if the widget isn't alive anymore)\n    if (g.ActiveId)\n        g.ActiveIdTimer += g.IO.DeltaTime;\n    g.LastActiveIdTimer += g.IO.DeltaTime;\n    g.ActiveIdPreviousFrame = g.ActiveId;\n    g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow;\n    g.ActiveIdPreviousFrameHasBeenEditedBefore = g.ActiveIdHasBeenEditedBefore;\n    g.ActiveIdIsAlive = 0;\n    g.ActiveIdHasBeenEditedThisFrame = false;\n    g.ActiveIdPreviousFrameIsAlive = false;\n    g.ActiveIdIsJustActivated = false;\n    if (g.TempInputId != 0 && g.ActiveId != g.TempInputId)\n        g.TempInputId = 0;\n    if (g.ActiveId == 0)\n    {\n        g.ActiveIdUsingNavDirMask = 0x00;\n        g.ActiveIdUsingAllKeyboardKeys = false;\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n        g.ActiveIdUsingNavInputMask = 0x00;\n#endif\n    }\n\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    if (g.ActiveId == 0)\n        g.ActiveIdUsingNavInputMask = 0;\n    else if (g.ActiveIdUsingNavInputMask != 0)\n    {\n        // If your custom widget code used:                 { g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel); }\n        // Since IMGUI_VERSION_NUM >= 18804 it should be:   { SetKeyOwner(ImGuiKey_Escape, g.ActiveId); SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId); }\n        if (g.ActiveIdUsingNavInputMask & (1 << ImGuiNavInput_Cancel))\n            SetKeyOwner(ImGuiKey_Escape, g.ActiveId);\n        if (g.ActiveIdUsingNavInputMask & ~(1 << ImGuiNavInput_Cancel))\n            IM_ASSERT(0); // Other values unsupported\n    }\n#endif\n\n    // Record when we have been stationary as this state is preserved while over same item.\n    // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.\n    // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.\n    if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)\n        g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;\n    else if (g.HoverItemDelayId == 0)\n        g.HoverItemUnlockedStationaryId = 0;\n    if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)\n        g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;\n    else if (g.HoveredWindow == NULL)\n        g.HoverWindowUnlockedStationaryId = 0;\n\n    // Update hover delay for IsItemHovered() with delays and tooltips\n    g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;\n    if (g.HoverItemDelayId != 0)\n    {\n        g.HoverItemDelayTimer += g.IO.DeltaTime;\n        g.HoverItemDelayClearTimer = 0.0f;\n        g.HoverItemDelayId = 0;\n    }\n    else if (g.HoverItemDelayTimer > 0.0f)\n    {\n        // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps\n        // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.\n        g.HoverItemDelayClearTimer += g.IO.DeltaTime;\n        if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate\n            g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer.\n    }\n\n    // Drag and drop\n    g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr;\n    g.DragDropAcceptIdCurr = 0;\n    g.DragDropAcceptIdCurrRectSurface = FLT_MAX;\n    g.DragDropWithinSource = false;\n    g.DragDropWithinTarget = false;\n    g.DragDropHoldJustPressedId = 0;\n\n    // Close popups on focus lost (currently wip/opt-in)\n    //if (g.IO.AppFocusLost)\n    //    ClosePopupsExceptModals();\n\n    // Update keyboard input state\n    UpdateKeyboardInputs();\n\n    //IM_ASSERT(g.IO.KeyCtrl == IsKeyDown(ImGuiKey_LeftCtrl) || IsKeyDown(ImGuiKey_RightCtrl));\n    //IM_ASSERT(g.IO.KeyShift == IsKeyDown(ImGuiKey_LeftShift) || IsKeyDown(ImGuiKey_RightShift));\n    //IM_ASSERT(g.IO.KeyAlt == IsKeyDown(ImGuiKey_LeftAlt) || IsKeyDown(ImGuiKey_RightAlt));\n    //IM_ASSERT(g.IO.KeySuper == IsKeyDown(ImGuiKey_LeftSuper) || IsKeyDown(ImGuiKey_RightSuper));\n\n    // Update gamepad/keyboard navigation\n    NavUpdate();\n\n    // Update mouse input state\n    UpdateMouseInputs();\n\n    // Find hovered window\n    // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame)\n    UpdateHoveredWindowAndCaptureFlags();\n\n    // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)\n    UpdateMouseMovingWindowNewFrame();\n\n    // Background darkening/whitening\n    if (GetTopMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))\n        g.DimBgRatio = ImMin(g.DimBgRatio + g.IO.DeltaTime * 6.0f, 1.0f);\n    else\n        g.DimBgRatio = ImMax(g.DimBgRatio - g.IO.DeltaTime * 10.0f, 0.0f);\n\n    g.MouseCursor = ImGuiMouseCursor_Arrow;\n    g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1;\n\n    // Platform IME data: reset for the frame\n    g.PlatformImeDataPrev = g.PlatformImeData;\n    g.PlatformImeData.WantVisible = false;\n\n    // Mouse wheel scrolling, scale\n    UpdateMouseWheel();\n\n    // Mark all windows as not visible and compact unused memory.\n    IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size);\n    const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer;\n    for (ImGuiWindow* window : g.Windows)\n    {\n        window->WasActive = window->Active;\n        window->Active = false;\n        window->WriteAccessed = false;\n        window->BeginCountPreviousFrame = window->BeginCount;\n        window->BeginCount = 0;\n\n        // Garbage collect transient buffers of recently unused windows\n        if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time)\n            GcCompactTransientWindowBuffers(window);\n    }\n\n    // Garbage collect transient buffers of recently unused tables\n    for (int i = 0; i < g.TablesLastTimeActive.Size; i++)\n        if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time)\n            TableGcCompactTransientBuffers(g.Tables.GetByIndex(i));\n    for (ImGuiTableTempData& table_temp_data : g.TablesTempData)\n        if (table_temp_data.LastTimeActive >= 0.0f && table_temp_data.LastTimeActive < memory_compact_start_time)\n            TableGcCompactTransientBuffers(&table_temp_data);\n    if (g.GcCompactAll)\n        GcCompactTransientMiscBuffers();\n    g.GcCompactAll = false;\n\n    // Closing the focused window restore focus to the first active root window in descending z-order\n    if (g.NavWindow && !g.NavWindow->WasActive)\n        FocusTopMostWindowUnderOne(NULL, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild);\n\n    // No window should be open at the beginning of the frame.\n    // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear.\n    g.CurrentWindowStack.resize(0);\n    g.BeginPopupStack.resize(0);\n    g.ItemFlagsStack.resize(0);\n    g.ItemFlagsStack.push_back(ImGuiItemFlags_None);\n    g.GroupStack.resize(0);\n\n    // [DEBUG] Update debug features\n    UpdateDebugToolItemPicker();\n    UpdateDebugToolStackQueries();\n    if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0)\n        g.DebugLocateId = 0;\n    if (g.DebugLogClipperAutoDisableFrames > 0 && --g.DebugLogClipperAutoDisableFrames == 0)\n    {\n        DebugLog(\"(Auto-disabled ImGuiDebugLogFlags_EventClipper to avoid spamming)\\n\");\n        g.DebugLogFlags &= ~ImGuiDebugLogFlags_EventClipper;\n    }\n\n    // Create implicit/fallback window - which we will only render it if the user has added something to it.\n    // We don't use \"Debug\" to avoid colliding with user trying to create a \"Debug\" window with custom flags.\n    // This fallback is particularly important as it prevents ImGui:: calls from crashing.\n    g.WithinFrameScopeWithImplicitWindow = true;\n    SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver);\n    Begin(\"Debug##Default\");\n    IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true);\n\n    // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack,\n    // allowing to validate correct Begin/End behavior in user code.\n    if (g.IO.ConfigDebugBeginReturnValueLoop)\n        g.DebugBeginReturnValueCullDepth = (g.DebugBeginReturnValueCullDepth == -1) ? 0 : ((g.DebugBeginReturnValueCullDepth + ((g.FrameCount % 4) == 0 ? 1 : 0)) % 10);\n    else\n        g.DebugBeginReturnValueCullDepth = -1;\n\n    CallContextHooks(&g, ImGuiContextHookType_NewFramePost);\n}\n\n// FIXME: Add a more explicit sort order in the window structure.\nstatic int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs)\n{\n    const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs;\n    const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs;\n    if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup))\n        return d;\n    if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip))\n        return d;\n    return (a->BeginOrderWithinParent - b->BeginOrderWithinParent);\n}\n\nstatic void AddWindowToSortBuffer(ImVector<ImGuiWindow*>* out_sorted_windows, ImGuiWindow* window)\n{\n    out_sorted_windows->push_back(window);\n    if (window->Active)\n    {\n        int count = window->DC.ChildWindows.Size;\n        ImQsort(window->DC.ChildWindows.Data, (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer);\n        for (int i = 0; i < count; i++)\n        {\n            ImGuiWindow* child = window->DC.ChildWindows[i];\n            if (child->Active)\n                AddWindowToSortBuffer(out_sorted_windows, child);\n        }\n    }\n}\n\nstatic void AddWindowToDrawData(ImGuiWindow* window, int layer)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiViewportP* viewport = g.Viewports[0];\n    g.IO.MetricsRenderWindows++;\n    if (window->DrawList->_Splitter._Count > 1)\n        window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows.\n    ImGui::AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[layer], window->DrawList);\n    for (ImGuiWindow* child : window->DC.ChildWindows)\n        if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active\n            AddWindowToDrawData(child, layer);\n}\n\nstatic inline int GetWindowDisplayLayer(ImGuiWindow* window)\n{\n    return (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0;\n}\n\n// Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu)\nstatic inline void AddRootWindowToDrawData(ImGuiWindow* window)\n{\n    AddWindowToDrawData(window, GetWindowDisplayLayer(window));\n}\n\nstatic void FlattenDrawDataIntoSingleLayer(ImDrawDataBuilder* builder)\n{\n    int n = builder->Layers[0]->Size;\n    int full_size = n;\n    for (int i = 1; i < IM_ARRAYSIZE(builder->Layers); i++)\n        full_size += builder->Layers[i]->Size;\n    builder->Layers[0]->resize(full_size);\n    for (int layer_n = 1; layer_n < IM_ARRAYSIZE(builder->Layers); layer_n++)\n    {\n        ImVector<ImDrawList*>* layer = builder->Layers[layer_n];\n        if (layer->empty())\n            continue;\n        memcpy(builder->Layers[0]->Data + n, layer->Data, layer->Size * sizeof(ImDrawList*));\n        n += layer->Size;\n        layer->resize(0);\n    }\n}\n\nstatic void InitViewportDrawData(ImGuiViewportP* viewport)\n{\n    ImGuiIO& io = ImGui::GetIO();\n    ImDrawData* draw_data = &viewport->DrawDataP;\n\n    viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists;\n    viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1;\n    viewport->DrawDataBuilder.Layers[0]->resize(0);\n    viewport->DrawDataBuilder.Layers[1]->resize(0);\n\n    draw_data->Valid = true;\n    draw_data->CmdListsCount = 0;\n    draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0;\n    draw_data->DisplayPos = viewport->Pos;\n    draw_data->DisplaySize = viewport->Size;\n    draw_data->FramebufferScale = io.DisplayFramebufferScale;\n    draw_data->OwnerViewport = viewport;\n}\n\n// Push a clipping rectangle for both ImGui logic (hit-testing etc.) and low-level ImDrawList rendering.\n// - When using this function it is sane to ensure that float are perfectly rounded to integer values,\n//   so that e.g. (int)(max.x-min.x) in user's render produce correct result.\n// - If the code here changes, may need to update code of functions like NextColumn() and PushColumnClipRect():\n//   some frequently called functions which to modify both channels and clipping simultaneously tend to use the\n//   more specialized SetWindowClipRectBeforeSetChannel() to avoid extraneous updates of underlying ImDrawCmds.\nvoid ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect);\n    window->ClipRect = window->DrawList->_ClipRectStack.back();\n}\n\nvoid ImGui::PopClipRect()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DrawList->PopClipRect();\n    window->ClipRect = window->DrawList->_ClipRectStack.back();\n}\n\nstatic void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport();\n    ImRect viewport_rect = viewport->GetMainRect();\n\n    // Draw behind window by moving the draw command at the FRONT of the draw list\n    {\n        // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows,\n        // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing.\n        // FIXME: This is creating complication, might be simpler if we could inject a drawlist in drawdata at a given position and not attempt to manipulate ImDrawCmd order.\n        ImDrawList* draw_list = window->RootWindow->DrawList;\n        if (draw_list->CmdBuffer.Size == 0)\n            draw_list->AddDrawCmd();\n        draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to stricty ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that)\n        draw_list->AddRectFilled(viewport_rect.Min, viewport_rect.Max, col);\n        ImDrawCmd cmd = draw_list->CmdBuffer.back();\n        IM_ASSERT(cmd.ElemCount == 6);\n        draw_list->CmdBuffer.pop_back();\n        draw_list->CmdBuffer.push_front(cmd);\n        draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command.\n        draw_list->PopClipRect();\n    }\n}\n\nImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* bottom_most_visible_window = parent_window;\n    for (int i = FindWindowDisplayIndex(parent_window); i >= 0; i--)\n    {\n        ImGuiWindow* window = g.Windows[i];\n        if (window->Flags & ImGuiWindowFlags_ChildWindow)\n            continue;\n        if (!IsWindowWithinBeginStackOf(window, parent_window))\n            break;\n        if (IsWindowActiveAndVisible(window) && GetWindowDisplayLayer(window) <= GetWindowDisplayLayer(parent_window))\n            bottom_most_visible_window = window;\n    }\n    return bottom_most_visible_window;\n}\n\nstatic void ImGui::RenderDimmedBackgrounds()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* modal_window = GetTopMostAndVisiblePopupModal();\n    if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)\n        return;\n    const bool dim_bg_for_modal = (modal_window != NULL);\n    const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL && g.NavWindowingTargetAnim->Active);\n    if (!dim_bg_for_modal && !dim_bg_for_window_list)\n        return;\n\n    if (dim_bg_for_modal)\n    {\n        // Draw dimming behind modal or a begin stack child, whichever comes first in draw order.\n        ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window);\n        RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(ImGuiCol_ModalWindowDimBg, g.DimBgRatio));\n    }\n    else if (dim_bg_for_window_list)\n    {\n        // Draw dimming behind CTRL+Tab target window\n        RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio));\n\n        // Draw border around CTRL+Tab target window\n        ImGuiWindow* window = g.NavWindowingTargetAnim;\n        ImGuiViewport* viewport = GetMainViewport();\n        float distance = g.FontSize;\n        ImRect bb = window->Rect();\n        bb.Expand(distance);\n        if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y)\n            bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward\n        if (window->DrawList->CmdBuffer.Size == 0)\n            window->DrawList->AddDrawCmd();\n        window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size);\n        window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f);\n        window->DrawList->PopClipRect();\n    }\n}\n\n// This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.\nvoid ImGui::EndFrame()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.Initialized);\n\n    // Don't process EndFrame() multiple times.\n    if (g.FrameCountEnded == g.FrameCount)\n        return;\n    IM_ASSERT(g.WithinFrameScope && \"Forgot to call ImGui::NewFrame()?\");\n\n    CallContextHooks(&g, ImGuiContextHookType_EndFramePre);\n\n    ErrorCheckEndFrameSanityChecks();\n\n    // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME)\n    ImGuiPlatformImeData* ime_data = &g.PlatformImeData;\n    if (g.IO.SetPlatformImeDataFn && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0)\n    {\n        IMGUI_DEBUG_LOG_IO(\"[io] Calling io.SetPlatformImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\\n\", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y);\n        ImGuiViewport* viewport = GetMainViewport();\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n        if (viewport->PlatformHandleRaw == NULL && g.IO.ImeWindowHandle != NULL)\n        {\n            viewport->PlatformHandleRaw = g.IO.ImeWindowHandle;\n            g.IO.SetPlatformImeDataFn(viewport, ime_data);\n            viewport->PlatformHandleRaw = NULL;\n        }\n        else\n#endif\n        {\n            g.IO.SetPlatformImeDataFn(viewport, ime_data);\n        }\n    }\n\n    // Hide implicit/fallback \"Debug\" window if it hasn't been used\n    g.WithinFrameScopeWithImplicitWindow = false;\n    if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed)\n        g.CurrentWindow->Active = false;\n    End();\n\n    // Update navigation: CTRL+Tab, wrap-around requests\n    NavEndFrame();\n\n    // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted)\n    if (g.DragDropActive)\n    {\n        bool is_delivered = g.DragDropPayload.Delivery;\n        bool is_elapsed = (g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) && ((g.DragDropSourceFlags & ImGuiDragDropFlags_SourceAutoExpirePayload) || !IsMouseDown(g.DragDropMouseButton));\n        if (is_delivered || is_elapsed)\n            ClearDragDrop();\n    }\n\n    // Drag and Drop: Fallback for source tooltip. This is not ideal but better than nothing.\n    if (g.DragDropActive && g.DragDropSourceFrameCount < g.FrameCount && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))\n    {\n        g.DragDropWithinSource = true;\n        SetTooltip(\"...\");\n        g.DragDropWithinSource = false;\n    }\n\n    // End frame\n    g.WithinFrameScope = false;\n    g.FrameCountEnded = g.FrameCount;\n\n    // Initiate moving window + handle left-click and right-click focus\n    UpdateMouseMovingWindowEndFrame();\n\n    // Sort the window list so that all child windows are after their parent\n    // We cannot do that on FocusWindow() because children may not exist yet\n    g.WindowsTempSortBuffer.resize(0);\n    g.WindowsTempSortBuffer.reserve(g.Windows.Size);\n    for (ImGuiWindow* window : g.Windows)\n    {\n        if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow))       // if a child is active its parent will add it\n            continue;\n        AddWindowToSortBuffer(&g.WindowsTempSortBuffer, window);\n    }\n\n    // This usually assert if there is a mismatch between the ImGuiWindowFlags_ChildWindow / ParentWindow values and DC.ChildWindows[] in parents, aka we've done something wrong.\n    IM_ASSERT(g.Windows.Size == g.WindowsTempSortBuffer.Size);\n    g.Windows.swap(g.WindowsTempSortBuffer);\n    g.IO.MetricsActiveWindows = g.WindowsActiveCount;\n\n    // Unlock font atlas\n    g.IO.Fonts->Locked = false;\n\n    // Clear Input data for next frame\n    g.IO.MousePosPrev = g.IO.MousePos;\n    g.IO.AppFocusLost = false;\n    g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f;\n    g.IO.InputQueueCharacters.resize(0);\n\n    CallContextHooks(&g, ImGuiContextHookType_EndFramePost);\n}\n\n// Prepare the data for rendering so you can call GetDrawData()\n// (As with anything within the ImGui:: namspace this doesn't touch your GPU or graphics API at all:\n// it is the role of the ImGui_ImplXXXX_RenderDrawData() function provided by the renderer backend)\nvoid ImGui::Render()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.Initialized);\n\n    if (g.FrameCountEnded != g.FrameCount)\n        EndFrame();\n    if (g.FrameCountRendered == g.FrameCount)\n        return;\n    g.FrameCountRendered = g.FrameCount;\n\n    g.IO.MetricsRenderWindows = 0;\n    CallContextHooks(&g, ImGuiContextHookType_RenderPre);\n\n    // Draw modal/window whitening backgrounds\n    RenderDimmedBackgrounds();\n\n    // Add background ImDrawList (for each active viewport)\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        InitViewportDrawData(viewport);\n        if (viewport->BgFgDrawLists[0] != NULL)\n            AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport));\n    }\n\n    // Add ImDrawList to render\n    ImGuiWindow* windows_to_render_top_most[2];\n    windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL;\n    windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL);\n    for (ImGuiWindow* window : g.Windows)\n    {\n        IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive \"warning C6011: Dereferencing NULL pointer 'window'\"\n        if (IsWindowActiveAndVisible(window) && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != windows_to_render_top_most[0] && window != windows_to_render_top_most[1])\n            AddRootWindowToDrawData(window);\n    }\n    for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++)\n        if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window\n            AddRootWindowToDrawData(windows_to_render_top_most[n]);\n\n    // Draw software mouse cursor if requested by io.MouseDrawCursor flag\n    if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None)\n        RenderMouseCursor(g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48));\n\n    // Setup ImDrawData structures for end-user\n    g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0;\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        FlattenDrawDataIntoSingleLayer(&viewport->DrawDataBuilder);\n\n        // Add foreground ImDrawList (for each active viewport)\n        if (viewport->BgFgDrawLists[1] != NULL)\n            AddDrawListToDrawDataEx(&viewport->DrawDataP, viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport));\n\n        // We call _PopUnusedDrawCmd() last thing, as RenderDimmedBackgrounds() rely on a valid command being there (especially in docking branch).\n        ImDrawData* draw_data = &viewport->DrawDataP;\n        IM_ASSERT(draw_data->CmdLists.Size == draw_data->CmdListsCount);\n        for (ImDrawList* draw_list : draw_data->CmdLists)\n            draw_list->_PopUnusedDrawCmd();\n\n        g.IO.MetricsRenderVertices += draw_data->TotalVtxCount;\n        g.IO.MetricsRenderIndices += draw_data->TotalIdxCount;\n    }\n\n    CallContextHooks(&g, ImGuiContextHookType_RenderPost);\n}\n\n// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker.\n// CalcTextSize(\"\") should return ImVec2(0.0f, g.FontSize)\nImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width)\n{\n    ImGuiContext& g = *GImGui;\n\n    const char* text_display_end;\n    if (hide_text_after_double_hash)\n        text_display_end = FindRenderedTextEnd(text, text_end);      // Hide anything after a '##' string\n    else\n        text_display_end = text_end;\n\n    ImFont* font = g.Font;\n    const float font_size = g.FontSize;\n    if (text == text_display_end)\n        return ImVec2(0.0f, font_size);\n    ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL);\n\n    // Round\n    // FIXME: This has been here since Dec 2015 (7b0bf230) but down the line we want this out.\n    // FIXME: Investigate using ceilf or e.g.\n    // - https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c\n    // - https://embarkstudios.github.io/rust-gpu/api/src/libm/math/ceilf.rs.html\n    text_size.x = IM_TRUNC(text_size.x + 0.99999f);\n\n    return text_size;\n}\n\n// Find window given position, search front-to-back\n// FIXME: Note that we have an inconsequential lag here: OuterRectClipped is updated in Begin(), so windows moved programmatically\n// with SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is\n// called, aka before the next Begin(). Moving window isn't affected.\nstatic void FindHoveredWindow()\n{\n    ImGuiContext& g = *GImGui;\n\n    ImGuiWindow* hovered_window = NULL;\n    ImGuiWindow* hovered_window_ignoring_moving_window = NULL;\n    if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs))\n        hovered_window = g.MovingWindow;\n\n    ImVec2 padding_regular = g.Style.TouchExtraPadding;\n    ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular;\n    for (int i = g.Windows.Size - 1; i >= 0; i--)\n    {\n        ImGuiWindow* window = g.Windows[i];\n        IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.\n        if (!window->Active || window->Hidden)\n            continue;\n        if (window->Flags & ImGuiWindowFlags_NoMouseInputs)\n            continue;\n\n        // Using the clipped AABB, a child window will typically be clipped by its parent (not always)\n        ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize;\n        if (!window->OuterRectClipped.ContainsWithPad(g.IO.MousePos, hit_padding))\n            continue;\n\n        // Support for one rectangular hole in any given window\n        // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512)\n        if (window->HitTestHoleSize.x != 0)\n        {\n            ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y);\n            ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y);\n            if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos))\n                continue;\n        }\n\n        if (hovered_window == NULL)\n            hovered_window = window;\n        IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer.\n        if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))\n            hovered_window_ignoring_moving_window = window;\n        if (hovered_window && hovered_window_ignoring_moving_window)\n            break;\n    }\n\n    g.HoveredWindow = hovered_window;\n    g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;\n}\n\nbool ImGui::IsItemActive()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveId)\n        return g.ActiveId == g.LastItemData.ID;\n    return false;\n}\n\nbool ImGui::IsItemActivated()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveId)\n        if (g.ActiveId == g.LastItemData.ID && g.ActiveIdPreviousFrame != g.LastItemData.ID)\n            return true;\n    return false;\n}\n\nbool ImGui::IsItemDeactivated()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDeactivated)\n        return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Deactivated) != 0;\n    return (g.ActiveIdPreviousFrame == g.LastItemData.ID && g.ActiveIdPreviousFrame != 0 && g.ActiveId != g.LastItemData.ID);\n}\n\nbool ImGui::IsItemDeactivatedAfterEdit()\n{\n    ImGuiContext& g = *GImGui;\n    return IsItemDeactivated() && (g.ActiveIdPreviousFrameHasBeenEditedBefore || (g.ActiveId == 0 && g.ActiveIdHasBeenEditedBefore));\n}\n\n// == GetItemID() == GetFocusID()\nbool ImGui::IsItemFocused()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.NavId != g.LastItemData.ID || g.NavId == 0)\n        return false;\n    return true;\n}\n\n// Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()!\n// Most widgets have specific reactions based on mouse-up/down state, mouse position etc.\nbool ImGui::IsItemClicked(ImGuiMouseButton mouse_button)\n{\n    return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None);\n}\n\nbool ImGui::IsItemToggledOpen()\n{\n    ImGuiContext& g = *GImGui;\n    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledOpen) ? true : false;\n}\n\nbool ImGui::IsItemToggledSelection()\n{\n    ImGuiContext& g = *GImGui;\n    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_ToggledSelection) ? true : false;\n}\n\n// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,\n// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!\n// Refer to FAQ entry \"How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?\" for details.\nbool ImGui::IsAnyItemHovered()\n{\n    ImGuiContext& g = *GImGui;\n    return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0;\n}\n\nbool ImGui::IsAnyItemActive()\n{\n    ImGuiContext& g = *GImGui;\n    return g.ActiveId != 0;\n}\n\nbool ImGui::IsAnyItemFocused()\n{\n    ImGuiContext& g = *GImGui;\n    return g.NavId != 0 && !g.NavDisableHighlight;\n}\n\nbool ImGui::IsItemVisible()\n{\n    ImGuiContext& g = *GImGui;\n    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0;\n}\n\nbool ImGui::IsItemEdited()\n{\n    ImGuiContext& g = *GImGui;\n    return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Edited) != 0;\n}\n\n// Allow next item to be overlapped by subsequent items.\n// This works by requiring HoveredId to match for two subsequent frames,\n// so if a following items overwrite it our interactions will naturally be disabled.\nvoid ImGui::SetNextItemAllowOverlap()\n{\n    ImGuiContext& g = *GImGui;\n    g.NextItemData.ItemFlags |= ImGuiItemFlags_AllowOverlap;\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority.\n// FIXME-LEGACY: Use SetNextItemAllowOverlap() *before* your item instead.\nvoid ImGui::SetItemAllowOverlap()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiID id = g.LastItemData.ID;\n    if (g.HoveredId == id)\n        g.HoveredIdAllowOverlap = true;\n    if (g.ActiveId == id) // Before we made this obsolete, most calls to SetItemAllowOverlap() used to avoid this path by testing g.ActiveId != id.\n        g.ActiveIdAllowOverlap = true;\n}\n#endif\n\n// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version for the two users of this function.\nvoid ImGui::SetActiveIdUsingAllKeyboardKeys()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.ActiveId != 0);\n    g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1;\n    g.ActiveIdUsingAllKeyboardKeys = true;\n    NavMoveRequestCancel();\n}\n\nImGuiID ImGui::GetItemID()\n{\n    ImGuiContext& g = *GImGui;\n    return g.LastItemData.ID;\n}\n\nImVec2 ImGui::GetItemRectMin()\n{\n    ImGuiContext& g = *GImGui;\n    return g.LastItemData.Rect.Min;\n}\n\nImVec2 ImGui::GetItemRectMax()\n{\n    ImGuiContext& g = *GImGui;\n    return g.LastItemData.Rect.Max;\n}\n\nImVec2 ImGui::GetItemRectSize()\n{\n    ImGuiContext& g = *GImGui;\n    return g.LastItemData.Rect.GetSize();\n}\n\n// Prior to v1.90 2023/10/16, the BeginChild() function took a 'bool border = false' parameter instead of 'ImGuiChildFlags child_flags = 0'.\n// ImGuiChildFlags_Border is defined as always == 1 in order to allow old code passing 'true'.\nbool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)\n{\n    ImGuiID id = GetCurrentWindow()->GetID(str_id);\n    return BeginChildEx(str_id, id, size_arg, child_flags, window_flags);\n}\n\nbool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)\n{\n    return BeginChildEx(NULL, id, size_arg, child_flags, window_flags);\n}\n\nbool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* parent_window = g.CurrentWindow;\n    IM_ASSERT(id != 0);\n\n    // Sanity check as it is likely that some user will accidentally pass ImGuiWindowFlags into the ImGuiChildFlags argument.\n    const ImGuiChildFlags ImGuiChildFlags_SupportedMask_ = ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_FrameStyle;\n    IM_UNUSED(ImGuiChildFlags_SupportedMask_);\n    IM_ASSERT((child_flags & ~ImGuiChildFlags_SupportedMask_) == 0 && \"Illegal ImGuiChildFlags value. Did you pass ImGuiWindowFlags values instead of ImGuiChildFlags?\");\n    IM_ASSERT((window_flags & ImGuiWindowFlags_AlwaysAutoResize) == 0 && \"Cannot specify ImGuiWindowFlags_AlwaysAutoResize for BeginChild(). Use ImGuiChildFlags_AlwaysAutoResize!\");\n    if (child_flags & ImGuiChildFlags_AlwaysAutoResize)\n    {\n        IM_ASSERT((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0 && \"Cannot use ImGuiChildFlags_ResizeX or ImGuiChildFlags_ResizeY with ImGuiChildFlags_AlwaysAutoResize!\");\n        IM_ASSERT((child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY)) != 0 && \"Must use ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY with ImGuiChildFlags_AlwaysAutoResize!\");\n    }\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    if (window_flags & ImGuiWindowFlags_AlwaysUseWindowPadding)\n        child_flags |= ImGuiChildFlags_AlwaysUseWindowPadding;\n#endif\n    if (child_flags & ImGuiChildFlags_AutoResizeX)\n        child_flags &= ~ImGuiChildFlags_ResizeX;\n    if (child_flags & ImGuiChildFlags_AutoResizeY)\n        child_flags &= ~ImGuiChildFlags_ResizeY;\n\n    // Set window flags\n    window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar;\n    window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag\n    if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize))\n        window_flags |= ImGuiWindowFlags_AlwaysAutoResize;\n    if ((child_flags & (ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY)) == 0)\n        window_flags |= ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings;\n\n    // Special framed style\n    if (child_flags & ImGuiChildFlags_FrameStyle)\n    {\n        PushStyleColor(ImGuiCol_ChildBg, g.Style.Colors[ImGuiCol_FrameBg]);\n        PushStyleVar(ImGuiStyleVar_ChildRounding, g.Style.FrameRounding);\n        PushStyleVar(ImGuiStyleVar_ChildBorderSize, g.Style.FrameBorderSize);\n        PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.FramePadding);\n        child_flags |= ImGuiChildFlags_Border | ImGuiChildFlags_AlwaysUseWindowPadding;\n        window_flags |= ImGuiWindowFlags_NoMove;\n    }\n\n    // Forward child flags\n    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags;\n    g.NextWindowData.ChildFlags = child_flags;\n\n    // Forward size\n    // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set.\n    // (the alternative would to store conditional flags per axis, which is possible but more code)\n    const ImVec2 size_avail = GetContentRegionAvail();\n    const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y);\n    const ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y);\n    SetNextWindowSize(size);\n\n    // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value.\n    // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround.\n    // e.g. \"ParentName###ParentIdentifier/ChildName###ChildIdentifier\" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it.\n    const char* temp_window_name;\n    /*if (name && parent_window->IDStack.back() == parent_window->ID)\n        ImFormatStringToTempBuffer(&temp_window_name, NULL, \"%s/%s\", parent_window->Name, name); // May omit ID if in root of ID stack\n    else*/\n    if (name)\n        ImFormatStringToTempBuffer(&temp_window_name, NULL, \"%s/%s_%08X\", parent_window->Name, name, id);\n    else\n        ImFormatStringToTempBuffer(&temp_window_name, NULL, \"%s/%08X\", parent_window->Name, id);\n\n    // Set style\n    const float backup_border_size = g.Style.ChildBorderSize;\n    if ((child_flags & ImGuiChildFlags_Border) == 0)\n        g.Style.ChildBorderSize = 0.0f;\n\n    // Begin into window\n    const bool ret = Begin(temp_window_name, NULL, window_flags);\n\n    // Restore style\n    g.Style.ChildBorderSize = backup_border_size;\n    if (child_flags & ImGuiChildFlags_FrameStyle)\n    {\n        PopStyleVar(3);\n        PopStyleColor();\n    }\n\n    ImGuiWindow* child_window = g.CurrentWindow;\n    child_window->ChildId = id;\n\n    // Set the cursor to handle case where the user called SetNextWindowPos()+BeginChild() manually.\n    // While this is not really documented/defined, it seems that the expected thing to do.\n    if (child_window->BeginCount == 1)\n        parent_window->DC.CursorPos = child_window->Pos;\n\n    // Process navigation-in immediately so NavInit can run on first frame\n    // Can enter a child if (A) it has navigable items or (B) it can be scrolled.\n    const ImGuiID temp_id_for_activation = ImHashStr(\"##Child\", 0, id);\n    if (g.ActiveId == temp_id_for_activation)\n        ClearActiveID();\n    if (g.NavActivateId == id && !(window_flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY))\n    {\n        FocusWindow(child_window);\n        NavInitWindow(child_window, false);\n        SetActiveID(temp_id_for_activation, child_window); // Steal ActiveId with another arbitrary id so that key-press won't activate child item\n        g.ActiveIdSource = g.NavInputSource;\n    }\n    return ret;\n}\n\nvoid ImGui::EndChild()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* child_window = g.CurrentWindow;\n\n    IM_ASSERT(g.WithinEndChild == false);\n    IM_ASSERT(child_window->Flags & ImGuiWindowFlags_ChildWindow);   // Mismatched BeginChild()/EndChild() calls\n\n    g.WithinEndChild = true;\n    ImVec2 child_size = child_window->Size;\n    End();\n    if (child_window->BeginCount == 1)\n    {\n        ImGuiWindow* parent_window = g.CurrentWindow;\n        ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + child_size);\n        ItemSize(child_size);\n        if ((child_window->DC.NavLayersActiveMask != 0 || child_window->DC.NavWindowHasScrollY) && !(child_window->Flags & ImGuiWindowFlags_NavFlattened))\n        {\n            ItemAdd(bb, child_window->ChildId);\n            RenderNavHighlight(bb, child_window->ChildId);\n\n            // When browsing a window that has no activable items (scroll only) we keep a highlight on the child (pass g.NavId to trick into always displaying)\n            if (child_window->DC.NavLayersActiveMask == 0 && child_window == g.NavWindow)\n                RenderNavHighlight(ImRect(bb.Min - ImVec2(2, 2), bb.Max + ImVec2(2, 2)), g.NavId, ImGuiNavHighlightFlags_TypeThin);\n        }\n        else\n        {\n            // Not navigable into\n            ItemAdd(bb, 0);\n\n            // But when flattened we directly reach items, adjust active layer mask accordingly\n            if (child_window->Flags & ImGuiWindowFlags_NavFlattened)\n                parent_window->DC.NavLayersActiveMaskNext |= child_window->DC.NavLayersActiveMaskNext;\n        }\n        if (g.HoveredWindow == child_window)\n            g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;\n    }\n    g.WithinEndChild = false;\n    g.LogLinePosY = -FLT_MAX; // To enforce a carriage return\n}\n\nstatic void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled)\n{\n    window->SetWindowPosAllowFlags       = enabled ? (window->SetWindowPosAllowFlags       | flags) : (window->SetWindowPosAllowFlags       & ~flags);\n    window->SetWindowSizeAllowFlags      = enabled ? (window->SetWindowSizeAllowFlags      | flags) : (window->SetWindowSizeAllowFlags      & ~flags);\n    window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);\n}\n\nImGuiWindow* ImGui::FindWindowByID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);\n}\n\nImGuiWindow* ImGui::FindWindowByName(const char* name)\n{\n    ImGuiID id = ImHashStr(name);\n    return FindWindowByID(id);\n}\n\nstatic void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)\n{\n    window->Pos = ImTrunc(ImVec2(settings->Pos.x, settings->Pos.y));\n    if (settings->Size.x > 0 && settings->Size.y > 0)\n        window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y));\n    window->Collapsed = settings->Collapsed;\n}\n\nstatic void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, ImGuiWindowFlags new_flags)\n{\n    ImGuiContext& g = *GImGui;\n\n    const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0);\n    const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild;\n    if ((just_created || child_flag_changed) && !new_is_explicit_child)\n    {\n        IM_ASSERT(!g.WindowsFocusOrder.contains(window));\n        g.WindowsFocusOrder.push_back(window);\n        window->FocusOrder = (short)(g.WindowsFocusOrder.Size - 1);\n    }\n    else if (!just_created && child_flag_changed && new_is_explicit_child)\n    {\n        IM_ASSERT(g.WindowsFocusOrder[window->FocusOrder] == window);\n        for (int n = window->FocusOrder + 1; n < g.WindowsFocusOrder.Size; n++)\n            g.WindowsFocusOrder[n]->FocusOrder--;\n        g.WindowsFocusOrder.erase(g.WindowsFocusOrder.Data + window->FocusOrder);\n        window->FocusOrder = -1;\n    }\n    window->IsExplicitChild = new_is_explicit_child;\n}\n\nstatic void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings)\n{\n    // Initial window state with e.g. default/arbitrary window position\n    // Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window.\n    const ImGuiViewport* main_viewport = ImGui::GetMainViewport();\n    window->Pos = main_viewport->Pos + ImVec2(60, 60);\n    window->Size = window->SizeFull = ImVec2(0, 0);\n    window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;\n\n    if (settings != NULL)\n    {\n        SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false);\n        ApplyWindowSettings(window, settings);\n    }\n    window->DC.CursorStartPos = window->DC.CursorMaxPos = window->DC.IdealMaxPos = window->Pos; // So first call to CalcWindowContentSizes() doesn't return crazy values\n\n    if ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0)\n    {\n        window->AutoFitFramesX = window->AutoFitFramesY = 2;\n        window->AutoFitOnlyGrows = false;\n    }\n    else\n    {\n        if (window->Size.x <= 0.0f)\n            window->AutoFitFramesX = 2;\n        if (window->Size.y <= 0.0f)\n            window->AutoFitFramesY = 2;\n        window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0);\n    }\n}\n\nstatic ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags)\n{\n    // Create window the first time\n    //IMGUI_DEBUG_LOG(\"CreateNewWindow '%s', flags = 0x%08X\\n\", name, flags);\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name);\n    window->Flags = flags;\n    g.WindowsById.SetVoidPtr(window->ID, window);\n\n    ImGuiWindowSettings* settings = NULL;\n    if (!(flags & ImGuiWindowFlags_NoSavedSettings))\n        if ((settings = ImGui::FindWindowSettingsByWindow(window)) != 0)\n            window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);\n\n    InitOrLoadWindowSettings(window, settings);\n\n    if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus)\n        g.Windows.push_front(window); // Quite slow but rare and only once\n    else\n        g.Windows.push_back(window);\n\n    return window;\n}\n\nstatic inline ImVec2 CalcWindowMinSize(ImGuiWindow* window)\n{\n    // Popups, menus and childs bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups)\n    // FIXME: the if/else could probably be removed, \"reduce artifacts\" section for all windows.\n    ImGuiContext& g = *GImGui;\n    ImVec2 size_min;\n    if (window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_ChildWindow))\n    {\n        size_min.x = (window->ChildFlags & ImGuiChildFlags_ResizeX) ? g.Style.WindowMinSize.x : 4.0f;\n        size_min.y = (window->ChildFlags & ImGuiChildFlags_ResizeY) ? g.Style.WindowMinSize.y : 4.0f;\n    }\n    else\n    {\n        ImGuiWindow* window_for_height = window;\n        size_min.x = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f;\n        size_min.y = ((window->Flags & ImGuiWindowFlags_AlwaysAutoResize) == 0) ? g.Style.WindowMinSize.x : 4.0f;\n        size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows\n    }\n    return size_min;\n}\n\nstatic ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& size_desired)\n{\n    ImGuiContext& g = *GImGui;\n    ImVec2 new_size = size_desired;\n    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)\n    {\n        // See comments in SetNextWindowSizeConstraints() for details about setting size_min an size_max.\n        ImRect cr = g.NextWindowData.SizeConstraintRect;\n        new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x;\n        new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y;\n        if (g.NextWindowData.SizeCallback)\n        {\n            ImGuiSizeCallbackData data;\n            data.UserData = g.NextWindowData.SizeCallbackUserData;\n            data.Pos = window->Pos;\n            data.CurrentSize = window->SizeFull;\n            data.DesiredSize = new_size;\n            g.NextWindowData.SizeCallback(&data);\n            new_size = data.DesiredSize;\n        }\n        new_size.x = IM_TRUNC(new_size.x);\n        new_size.y = IM_TRUNC(new_size.y);\n    }\n\n    // Minimum size\n    ImVec2 size_min = CalcWindowMinSize(window);\n    return ImMax(new_size, size_min);\n}\n\nstatic void CalcWindowContentSizes(ImGuiWindow* window, ImVec2* content_size_current, ImVec2* content_size_ideal)\n{\n    bool preserve_old_content_sizes = false;\n    if (window->Collapsed && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)\n        preserve_old_content_sizes = true;\n    else if (window->Hidden && window->HiddenFramesCannotSkipItems == 0 && window->HiddenFramesCanSkipItems > 0)\n        preserve_old_content_sizes = true;\n    if (preserve_old_content_sizes)\n    {\n        *content_size_current = window->ContentSize;\n        *content_size_ideal = window->ContentSizeIdeal;\n        return;\n    }\n\n    content_size_current->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(window->DC.CursorMaxPos.x - window->DC.CursorStartPos.x);\n    content_size_current->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(window->DC.CursorMaxPos.y - window->DC.CursorStartPos.y);\n    content_size_ideal->x = (window->ContentSizeExplicit.x != 0.0f) ? window->ContentSizeExplicit.x : IM_TRUNC(ImMax(window->DC.CursorMaxPos.x, window->DC.IdealMaxPos.x) - window->DC.CursorStartPos.x);\n    content_size_ideal->y = (window->ContentSizeExplicit.y != 0.0f) ? window->ContentSizeExplicit.y : IM_TRUNC(ImMax(window->DC.CursorMaxPos.y, window->DC.IdealMaxPos.y) - window->DC.CursorStartPos.y);\n}\n\nstatic ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_contents)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n    const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x;\n    const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y;\n    ImVec2 size_pad = window->WindowPadding * 2.0f;\n    ImVec2 size_desired = size_contents + size_pad + ImVec2(decoration_w_without_scrollbars, decoration_h_without_scrollbars);\n    if (window->Flags & ImGuiWindowFlags_Tooltip)\n    {\n        // Tooltip always resize\n        return size_desired;\n    }\n    else\n    {\n        // Maximum window size is determined by the viewport size or monitor size\n        ImVec2 size_min = CalcWindowMinSize(window);\n        ImVec2 avail_size = ImGui::GetMainViewport()->WorkSize;\n        ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f));\n\n        // When the window cannot fit all contents (either because of constraints, either because screen is too small),\n        // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding.\n        ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit);\n        bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x  && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar);\n        bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar);\n        if (will_have_scrollbar_x)\n            size_auto_fit.y += style.ScrollbarSize;\n        if (will_have_scrollbar_y)\n            size_auto_fit.x += style.ScrollbarSize;\n        return size_auto_fit;\n    }\n}\n\nImVec2 ImGui::CalcWindowNextAutoFitSize(ImGuiWindow* window)\n{\n    ImVec2 size_contents_current;\n    ImVec2 size_contents_ideal;\n    CalcWindowContentSizes(window, &size_contents_current, &size_contents_ideal);\n    ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, size_contents_ideal);\n    ImVec2 size_final = CalcWindowSizeAfterConstraint(window, size_auto_fit);\n    return size_final;\n}\n\nstatic ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window)\n{\n    if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup))\n        return ImGuiCol_PopupBg;\n    if (window->Flags & ImGuiWindowFlags_ChildWindow)\n        return ImGuiCol_ChildBg;\n    return ImGuiCol_WindowBg;\n}\n\nstatic void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size)\n{\n    ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm);                // Expected window upper-left\n    ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right\n    ImVec2 size_expected = pos_max - pos_min;\n    ImVec2 size_constrained = CalcWindowSizeAfterConstraint(window, size_expected);\n    *out_pos = pos_min;\n    if (corner_norm.x == 0.0f)\n        out_pos->x -= (size_constrained.x - size_expected.x);\n    if (corner_norm.y == 0.0f)\n        out_pos->y -= (size_constrained.y - size_expected.y);\n    *out_size = size_constrained;\n}\n\n// Data for resizing from resize grip / corner\nstruct ImGuiResizeGripDef\n{\n    ImVec2  CornerPosN;\n    ImVec2  InnerDir;\n    int     AngleMin12, AngleMax12;\n};\nstatic const ImGuiResizeGripDef resize_grip_def[4] =\n{\n    { ImVec2(1, 1), ImVec2(-1, -1), 0, 3 },  // Lower-right\n    { ImVec2(0, 1), ImVec2(+1, -1), 3, 6 },  // Lower-left\n    { ImVec2(0, 0), ImVec2(+1, +1), 6, 9 },  // Upper-left (Unused)\n    { ImVec2(1, 0), ImVec2(-1, +1), 9, 12 }  // Upper-right (Unused)\n};\n\n// Data for resizing from borders\nstruct ImGuiResizeBorderDef\n{\n    ImVec2  InnerDir;               // Normal toward inside\n    ImVec2  SegmentN1, SegmentN2;   // End positions, normalized (0,0: upper left)\n    float   OuterAngle;             // Angle toward outside\n};\nstatic const ImGuiResizeBorderDef resize_border_def[4] =\n{\n    { ImVec2(+1, 0), ImVec2(0, 1), ImVec2(0, 0), IM_PI * 1.00f }, // Left\n    { ImVec2(-1, 0), ImVec2(1, 0), ImVec2(1, 1), IM_PI * 0.00f }, // Right\n    { ImVec2(0, +1), ImVec2(0, 0), ImVec2(1, 0), IM_PI * 1.50f }, // Up\n    { ImVec2(0, -1), ImVec2(1, 1), ImVec2(0, 1), IM_PI * 0.50f }  // Down\n};\n\nstatic ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness)\n{\n    ImRect rect = window->Rect();\n    if (thickness == 0.0f)\n        rect.Max -= ImVec2(1, 1);\n    if (border_n == ImGuiDir_Left)  { return ImRect(rect.Min.x - thickness,    rect.Min.y + perp_padding, rect.Min.x + thickness,    rect.Max.y - perp_padding); }\n    if (border_n == ImGuiDir_Right) { return ImRect(rect.Max.x - thickness,    rect.Min.y + perp_padding, rect.Max.x + thickness,    rect.Max.y - perp_padding); }\n    if (border_n == ImGuiDir_Up)    { return ImRect(rect.Min.x + perp_padding, rect.Min.y - thickness,    rect.Max.x - perp_padding, rect.Min.y + thickness);    }\n    if (border_n == ImGuiDir_Down)  { return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness,    rect.Max.x - perp_padding, rect.Max.y + thickness);    }\n    IM_ASSERT(0);\n    return ImRect();\n}\n\n// 0..3: corners (Lower-right, Lower-left, Unused, Unused)\nImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n)\n{\n    IM_ASSERT(n >= 0 && n < 4);\n    ImGuiID id = window->ID;\n    id = ImHashStr(\"#RESIZE\", 0, id);\n    id = ImHashData(&n, sizeof(int), id);\n    return id;\n}\n\n// Borders (Left, Right, Up, Down)\nImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir)\n{\n    IM_ASSERT(dir >= 0 && dir < 4);\n    int n = (int)dir + 4;\n    ImGuiID id = window->ID;\n    id = ImHashStr(\"#RESIZE\", 0, id);\n    id = ImHashData(&n, sizeof(int), id);\n    return id;\n}\n\n// Handle resize for: Resize Grips, Borders, Gamepad\n// Return true when using auto-fit (double-click on resize grip)\nstatic int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_hovered, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindowFlags flags = window->Flags;\n\n    if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)\n        return false;\n    if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window.\n        return false;\n\n    int ret_auto_fit_mask = 0x00;\n    const float grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));\n    const float grip_hover_inner_size = IM_TRUNC(grip_draw_size * 0.75f);\n    const float grip_hover_outer_size = g.IO.ConfigWindowsResizeFromEdges ? WINDOWS_HOVER_PADDING : 0.0f;\n\n    ImRect clamp_rect = visibility_rect;\n    const bool window_move_from_title_bar = g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar);\n    if (window_move_from_title_bar)\n        clamp_rect.Min.y -= window->TitleBarHeight();\n\n    ImVec2 pos_target(FLT_MAX, FLT_MAX);\n    ImVec2 size_target(FLT_MAX, FLT_MAX);\n\n    // Resize grips and borders are on layer 1\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;\n\n    // Manual resize grips\n    PushID(\"#RESIZE\");\n    for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)\n    {\n        const ImGuiResizeGripDef& def = resize_grip_def[resize_grip_n];\n        const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, def.CornerPosN);\n\n        // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window\n        bool hovered, held;\n        ImRect resize_rect(corner - def.InnerDir * grip_hover_outer_size, corner + def.InnerDir * grip_hover_inner_size);\n        if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x);\n        if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y);\n        ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID()\n        ItemAdd(resize_rect, resize_grip_id, NULL, ImGuiItemFlags_NoNav);\n        ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);\n        //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255));\n        if (hovered || held)\n            g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE;\n\n        if (held && g.IO.MouseDoubleClicked[0])\n        {\n            // Auto-fit when double-clicking\n            size_target = CalcWindowSizeAfterConstraint(window, size_auto_fit);\n            ret_auto_fit_mask = 0x03; // Both axises\n            ClearActiveID();\n        }\n        else if (held)\n        {\n            // Resize from any of the four corners\n            // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position\n            ImVec2 clamp_min = ImVec2(def.CornerPosN.x == 1.0f ? clamp_rect.Min.x : -FLT_MAX, (def.CornerPosN.y == 1.0f || (def.CornerPosN.y == 0.0f && window_move_from_title_bar)) ? clamp_rect.Min.y : -FLT_MAX);\n            ImVec2 clamp_max = ImVec2(def.CornerPosN.x == 0.0f ? clamp_rect.Max.x : +FLT_MAX, def.CornerPosN.y == 0.0f ? clamp_rect.Max.y : +FLT_MAX);\n            ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + ImLerp(def.InnerDir * grip_hover_outer_size, def.InnerDir * -grip_hover_inner_size, def.CornerPosN); // Corner of the window corresponding to our corner grip\n            corner_target = ImClamp(corner_target, clamp_min, clamp_max);\n            CalcResizePosSizeFromAnyCorner(window, corner_target, def.CornerPosN, &pos_target, &size_target);\n        }\n\n        // Only lower-left grip is visible before hovering/activating\n        if (resize_grip_n == 0 || held || hovered)\n            resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip);\n    }\n\n    int resize_border_mask = 0x00;\n    if (window->Flags & ImGuiWindowFlags_ChildWindow)\n        resize_border_mask |= ((window->ChildFlags & ImGuiChildFlags_ResizeX) ? 0x02 : 0) | ((window->ChildFlags & ImGuiChildFlags_ResizeY) ? 0x08 : 0);\n    else\n        resize_border_mask = g.IO.ConfigWindowsResizeFromEdges ? 0x0F : 0x00;\n    for (int border_n = 0; border_n < 4; border_n++)\n    {\n        if ((resize_border_mask & (1 << border_n)) == 0)\n            continue;\n        const ImGuiResizeBorderDef& def = resize_border_def[border_n];\n        const ImGuiAxis axis = (border_n == ImGuiDir_Left || border_n == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y;\n\n        bool hovered, held;\n        ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING);\n        ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID()\n        ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav);\n        ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus);\n        //GetForegroundDrawList(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255));\n        if (hovered && g.HoveredIdTimer <= WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)\n            hovered = false;\n        if (hovered || held)\n            g.MouseCursor = (axis == ImGuiAxis_X) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS;\n        if (held && g.IO.MouseDoubleClicked[0])\n        {\n            // Double-clicking bottom or right border auto-fit on this axis\n            // FIXME: Support top and right borders: rework CalcResizePosSizeFromAnyCorner() to be reusable in both cases.\n            if (border_n == 1 || border_n == 3) // Right and bottom border\n            {\n                size_target[axis] = CalcWindowSizeAfterConstraint(window, size_auto_fit)[axis];\n                ret_auto_fit_mask |= (1 << axis);\n                hovered = held = false; // So border doesn't show highlighted at new position\n            }\n            ClearActiveID();\n        }\n        else if (held)\n        {\n            // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop.\n            // Currently only using relative mode on resizable child windows, as the problem to solve is more likely noticeable for them, but could apply for all windows eventually.\n            // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it!\n            const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false));\n            if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing)\n            {\n                g.WindowResizeBorderExpectedRect = border_rect;\n                g.WindowResizeRelativeMode = false;\n            }\n            if ((window->Flags & ImGuiWindowFlags_ChildWindow) && memcmp(&g.WindowResizeBorderExpectedRect, &border_rect, sizeof(ImRect)) != 0)\n                g.WindowResizeRelativeMode = true;\n\n            const ImVec2 border_curr = (window->Pos + ImMin(def.SegmentN1, def.SegmentN2) * window->Size);\n            const float border_target_rel_mode_for_axis = border_curr[axis] + g.IO.MouseDelta[axis];\n            const float border_target_abs_mode_for_axis = g.IO.MousePos[axis] - g.ActiveIdClickOffset[axis] + WINDOWS_HOVER_PADDING; // Match ButtonBehavior() padding above.\n\n            // Use absolute mode position\n            ImVec2 border_target = window->Pos;\n            border_target[axis] = border_target_abs_mode_for_axis;\n\n            // Use relative mode target for child window, ignore resize when moving back toward the ideal absolute position.\n            bool ignore_resize = false;\n            if (g.WindowResizeRelativeMode)\n            {\n                //GetForegroundDrawList()->AddText(GetMainViewport()->WorkPos, IM_COL32_WHITE, \"Relative Mode\");\n                border_target[axis] = border_target_rel_mode_for_axis;\n                if (g.IO.MouseDelta[axis] == 0.0f || (g.IO.MouseDelta[axis] > 0.0f) == (border_target_rel_mode_for_axis > border_target_abs_mode_for_axis))\n                    ignore_resize = true;\n            }\n\n            // Clamp, apply\n            ImVec2 clamp_min(border_n == ImGuiDir_Right ? clamp_rect.Min.x : -FLT_MAX, border_n == ImGuiDir_Down || (border_n == ImGuiDir_Up && window_move_from_title_bar) ? clamp_rect.Min.y : -FLT_MAX);\n            ImVec2 clamp_max(border_n == ImGuiDir_Left ? clamp_rect.Max.x : +FLT_MAX, border_n == ImGuiDir_Up ? clamp_rect.Max.y : +FLT_MAX);\n            border_target = ImClamp(border_target, clamp_min, clamp_max);\n            if (flags & ImGuiWindowFlags_ChildWindow) // Clamp resizing of childs within parent\n            {\n                if ((flags & (ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar)) == 0 || (flags & ImGuiWindowFlags_NoScrollbar))\n                    border_target.x = ImClamp(border_target.x, window->ParentWindow->InnerClipRect.Min.x, window->ParentWindow->InnerClipRect.Max.x);\n                if (flags & ImGuiWindowFlags_NoScrollbar)\n                    border_target.y = ImClamp(border_target.y, window->ParentWindow->InnerClipRect.Min.y, window->ParentWindow->InnerClipRect.Max.y);\n            }\n            if (!ignore_resize)\n                CalcResizePosSizeFromAnyCorner(window, border_target, ImMin(def.SegmentN1, def.SegmentN2), &pos_target, &size_target);\n        }\n        if (hovered)\n            *border_hovered = border_n;\n        if (held)\n            *border_held = border_n;\n    }\n    PopID();\n\n    // Restore nav layer\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n\n    // Navigation resize (keyboard/gamepad)\n    // FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user.\n    // Not even sure the callback works here.\n    if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window)\n    {\n        ImVec2 nav_resize_dir;\n        if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift)\n            nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);\n        if (g.NavInputSource == ImGuiInputSource_Gamepad)\n            nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown);\n        if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f)\n        {\n            const float NAV_RESIZE_SPEED = 600.0f;\n            const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y);\n            g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step;\n            g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, clamp_rect.Min - window->Pos - window->Size); // We need Pos+Size >= clmap_rect.Min, so Size >= clmap_rect.Min - Pos, so size_delta >= clmap_rect.Min - window->Pos - window->Size\n            g.NavWindowingToggleLayer = false;\n            g.NavDisableMouseHover = true;\n            resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive);\n            ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaSize);\n            if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)\n            {\n                // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck.\n                size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored);\n                g.NavWindowingAccumDeltaSize -= accum_floored;\n            }\n        }\n    }\n\n    // Apply back modified position/size to window\n    if (size_target.x != FLT_MAX)\n        window->Size.x = window->SizeFull.x = size_target.x;\n    if (size_target.y != FLT_MAX)\n        window->Size.y = window->SizeFull.y = size_target.y;\n    if (pos_target.x != FLT_MAX)\n        window->Pos.x = ImTrunc(pos_target.x);\n    if (pos_target.y != FLT_MAX)\n        window->Pos.y = ImTrunc(pos_target.y);\n    if (size_target.x != FLT_MAX || size_target.y != FLT_MAX || pos_target.x != FLT_MAX || pos_target.y != FLT_MAX)\n        MarkIniSettingsDirty(window);\n\n    // Recalculate next expected border expected coordinates\n    if (*border_held != -1)\n        g.WindowResizeBorderExpectedRect = GetResizeBorderRect(window, *border_held, grip_hover_inner_size, WINDOWS_HOVER_PADDING);\n\n    return ret_auto_fit_mask;\n}\n\nstatic inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect)\n{\n    ImGuiContext& g = *GImGui;\n    ImVec2 size_for_clamping = window->Size;\n    if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar))\n        size_for_clamping.y = window->TitleBarHeight();\n    window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max);\n}\n\nstatic void ImGui::RenderWindowOuterBorders(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    float rounding = window->WindowRounding;\n    float border_size = window->WindowBorderSize;\n    if (border_size > 0.0f && !(window->Flags & ImGuiWindowFlags_NoBackground))\n        window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), rounding, 0, border_size);\n\n    if (window->ResizeBorderHovered != -1 || window->ResizeBorderHeld != -1)\n    {\n        const int border_n = (window->ResizeBorderHeld != -1) ? window->ResizeBorderHeld : window->ResizeBorderHovered;\n        const ImGuiResizeBorderDef& def = resize_border_def[border_n];\n        const ImRect border_r = GetResizeBorderRect(window, border_n, rounding, 0.0f);\n        const ImU32 border_col = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);\n        window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN1) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle - IM_PI * 0.25f, def.OuterAngle);\n        window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.SegmentN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f);\n        window->DrawList->PathStroke(border_col, 0, ImMax(2.0f, border_size)); // Thicker than usual\n    }\n    if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar))\n    {\n        float y = window->Pos.y + window->TitleBarHeight() - 1;\n        window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize);\n    }\n}\n\n// Draw background and borders\n// Draw and handle scrollbars\nvoid ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n    ImGuiWindowFlags flags = window->Flags;\n\n    // Ensure that ScrollBar doesn't read last frame's SkipItems\n    IM_ASSERT(window->BeginCount == 0);\n    window->SkipItems = false;\n\n    // Draw window + handle manual resize\n    // As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame.\n    const float window_rounding = window->WindowRounding;\n    const float window_border_size = window->WindowBorderSize;\n    if (window->Collapsed)\n    {\n        // Title bar only\n        const float backup_border_size = style.FrameBorderSize;\n        g.Style.FrameBorderSize = window->WindowBorderSize;\n        ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed);\n        RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding);\n        g.Style.FrameBorderSize = backup_border_size;\n    }\n    else\n    {\n        // Window background\n        if (!(flags & ImGuiWindowFlags_NoBackground))\n        {\n            ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window));\n            bool override_alpha = false;\n            float alpha = 1.0f;\n            if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha)\n            {\n                alpha = g.NextWindowData.BgAlphaVal;\n                override_alpha = true;\n            }\n            if (override_alpha)\n                bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT);\n            window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom);\n        }\n\n        // Title bar\n        if (!(flags & ImGuiWindowFlags_NoTitleBar))\n        {\n            ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);\n            window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop);\n        }\n\n        // Menu bar\n        if (flags & ImGuiWindowFlags_MenuBar)\n        {\n            ImRect menu_bar_rect = window->MenuBarRect();\n            menu_bar_rect.ClipWith(window->Rect());  // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them.\n            window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop);\n            if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y)\n                window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);\n        }\n\n        // Scrollbars\n        if (window->ScrollbarX)\n            Scrollbar(ImGuiAxis_X);\n        if (window->ScrollbarY)\n            Scrollbar(ImGuiAxis_Y);\n\n        // Render resize grips (after their input handling so we don't have a frame of latency)\n        if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize))\n        {\n            for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++)\n            {\n                const ImU32 col = resize_grip_col[resize_grip_n];\n                if ((col & IM_COL32_A_MASK) == 0)\n                    continue;\n                const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n];\n                const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN);\n                window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size)));\n                window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size)));\n                window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12);\n                window->DrawList->PathFillConvex(col);\n            }\n        }\n\n        // Borders\n        if (handle_borders_and_resize_grips)\n            RenderWindowOuterBorders(window);\n    }\n}\n\n// Render title text, collapse button, close button\nvoid ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n    ImGuiWindowFlags flags = window->Flags;\n\n    const bool has_close_button = (p_open != NULL);\n    const bool has_collapse_button = !(flags & ImGuiWindowFlags_NoCollapse) && (style.WindowMenuButtonPosition != ImGuiDir_None);\n\n    // Close & Collapse button are on the Menu NavLayer and don't default focus (unless there's nothing else on that layer)\n    // FIXME-NAV: Might want (or not?) to set the equivalent of ImGuiButtonFlags_NoNavFocus so that mouse clicks on standard title bar items don't necessarily set nav/keyboard ref?\n    const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags;\n    g.CurrentItemFlags |= ImGuiItemFlags_NoNavDefaultFocus;\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;\n\n    // Layout buttons\n    // FIXME: Would be nice to generalize the subtleties expressed here into reusable code.\n    float pad_l = style.FramePadding.x;\n    float pad_r = style.FramePadding.x;\n    float button_sz = g.FontSize;\n    ImVec2 close_button_pos;\n    ImVec2 collapse_button_pos;\n    if (has_close_button)\n    {\n        close_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);\n        pad_r += button_sz + style.ItemInnerSpacing.x;\n    }\n    if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Right)\n    {\n        collapse_button_pos = ImVec2(title_bar_rect.Max.x - pad_r - button_sz, title_bar_rect.Min.y + style.FramePadding.y);\n        pad_r += button_sz + style.ItemInnerSpacing.x;\n    }\n    if (has_collapse_button && style.WindowMenuButtonPosition == ImGuiDir_Left)\n    {\n        collapse_button_pos = ImVec2(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y + style.FramePadding.y);\n        pad_l += button_sz + style.ItemInnerSpacing.x;\n    }\n\n    // Collapse button (submitting first so it gets priority when choosing a navigation init fallback)\n    if (has_collapse_button)\n        if (CollapseButton(window->GetID(\"#COLLAPSE\"), collapse_button_pos))\n            window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function\n\n    // Close button\n    if (has_close_button)\n        if (CloseButton(window->GetID(\"#CLOSE\"), close_button_pos))\n            *p_open = false;\n\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n    g.CurrentItemFlags = item_flags_backup;\n\n    // Title bar text (with: horizontal alignment, avoiding collapse/close button, optional \"unsaved document\" marker)\n    // FIXME: Refactor text alignment facilities along with RenderText helpers, this is WAY too much messy code..\n    const float marker_size_x = (flags & ImGuiWindowFlags_UnsavedDocument) ? button_sz * 0.80f : 0.0f;\n    const ImVec2 text_size = CalcTextSize(name, NULL, true) + ImVec2(marker_size_x, 0.0f);\n\n    // As a nice touch we try to ensure that centered title text doesn't get affected by visibility of Close/Collapse button,\n    // while uncentered title text will still reach edges correctly.\n    if (pad_l > style.FramePadding.x)\n        pad_l += g.Style.ItemInnerSpacing.x;\n    if (pad_r > style.FramePadding.x)\n        pad_r += g.Style.ItemInnerSpacing.x;\n    if (style.WindowTitleAlign.x > 0.0f && style.WindowTitleAlign.x < 1.0f)\n    {\n        float centerness = ImSaturate(1.0f - ImFabs(style.WindowTitleAlign.x - 0.5f) * 2.0f); // 0.0f on either edges, 1.0f on center\n        float pad_extend = ImMin(ImMax(pad_l, pad_r), title_bar_rect.GetWidth() - pad_l - pad_r - text_size.x);\n        pad_l = ImMax(pad_l, pad_extend * centerness);\n        pad_r = ImMax(pad_r, pad_extend * centerness);\n    }\n\n    ImRect layout_r(title_bar_rect.Min.x + pad_l, title_bar_rect.Min.y, title_bar_rect.Max.x - pad_r, title_bar_rect.Max.y);\n    ImRect clip_r(layout_r.Min.x, layout_r.Min.y, ImMin(layout_r.Max.x + g.Style.ItemInnerSpacing.x, title_bar_rect.Max.x), layout_r.Max.y);\n    if (flags & ImGuiWindowFlags_UnsavedDocument)\n    {\n        ImVec2 marker_pos;\n        marker_pos.x = ImClamp(layout_r.Min.x + (layout_r.GetWidth() - text_size.x) * style.WindowTitleAlign.x + text_size.x, layout_r.Min.x, layout_r.Max.x);\n        marker_pos.y = (layout_r.Min.y + layout_r.Max.y) * 0.5f;\n        if (marker_pos.x > layout_r.Min.x)\n        {\n            RenderBullet(window->DrawList, marker_pos, GetColorU32(ImGuiCol_Text));\n            clip_r.Max.x = ImMin(clip_r.Max.x, marker_pos.x - (int)(marker_size_x * 0.5f));\n        }\n    }\n    //if (g.IO.KeyShift) window->DrawList->AddRect(layout_r.Min, layout_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]\n    //if (g.IO.KeyCtrl) window->DrawList->AddRect(clip_r.Min, clip_r.Max, IM_COL32(255, 128, 0, 255)); // [DEBUG]\n    RenderTextClipped(layout_r.Min, layout_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_r);\n}\n\nvoid ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)\n{\n    window->ParentWindow = parent_window;\n    window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;\n    if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))\n        window->RootWindow = parent_window->RootWindow;\n    if (parent_window && (flags & ImGuiWindowFlags_Popup))\n        window->RootWindowPopupTree = parent_window->RootWindowPopupTree;\n    if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))\n        window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;\n    while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)\n    {\n        IM_ASSERT(window->RootWindowForNav->ParentWindow != NULL);\n        window->RootWindowForNav = window->RootWindowForNav->ParentWindow;\n    }\n}\n\n// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)\n// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.\n// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.\n// - WindowA            // FindBlockingModal() returns Modal1\n//   - WindowB          //                  .. returns Modal1\n//   - Modal1           //                  .. returns Modal2\n//      - WindowC       //                  .. returns Modal2\n//          - WindowD   //                  .. returns Modal2\n//          - Modal2    //                  .. returns Modal2\n//            - WindowE //                  .. returns NULL\n// Notes:\n// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.\n//   Only difference is here we check for ->Active/WasActive but it may be unecessary.\nImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.OpenPopupStack.Size <= 0)\n        return NULL;\n\n    // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.\n    for (ImGuiPopupData& popup_data : g.OpenPopupStack)\n    {\n        ImGuiWindow* popup_window = popup_data.Window;\n        if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))\n            continue;\n        if (!popup_window->Active && !popup_window->WasActive)      // Check WasActive, because this code may run before popup renders on current frame, also check Active to handle newly created windows.\n            continue;\n        if (window == NULL)                                         // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.\n            return popup_window;\n        if (IsWindowWithinBeginStackOf(window, popup_window))       // Window may be over modal\n            continue;\n        return popup_window;                                        // Place window right below first block modal\n    }\n    return NULL;\n}\n\n// Push a new Dear ImGui window to add widgets to.\n// - A default window called \"Debug\" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair.\n// - Begin/End can be called multiple times during the frame with the same window name to append content.\n// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file).\n//   You can use the \"##\" or \"###\" markers to use the same label with different id, or same id with different label. See documentation at the top of this file.\n// - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned.\n// - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed.\nbool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    IM_ASSERT(name != NULL && name[0] != '\\0');     // Window name required\n    IM_ASSERT(g.WithinFrameScope);                  // Forgot to call ImGui::NewFrame()\n    IM_ASSERT(g.FrameCountEnded != g.FrameCount);   // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet\n\n    // Find or create\n    ImGuiWindow* window = FindWindowByName(name);\n    const bool window_just_created = (window == NULL);\n    if (window_just_created)\n        window = CreateNewWindow(name, flags);\n\n    // Automatically disable manual moving/resizing when NoInputs is set\n    if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs)\n        flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;\n\n    if (flags & ImGuiWindowFlags_NavFlattened)\n        IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow);\n\n    const int current_frame = g.FrameCount;\n    const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame);\n    window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow);\n\n    // Update the Appearing flag\n    bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1);   // Not using !WasActive because the implicit \"Debug\" window would always toggle off->on\n    if (flags & ImGuiWindowFlags_Popup)\n    {\n        ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];\n        window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed\n        window_just_activated_by_user |= (window != popup_ref.Window);\n    }\n    window->Appearing = window_just_activated_by_user;\n    if (window->Appearing)\n        SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);\n\n    // Update Flags, LastFrameActive, BeginOrderXXX fields\n    if (first_begin_of_the_frame)\n    {\n        UpdateWindowInFocusOrderList(window, window_just_created, flags);\n        window->Flags = (ImGuiWindowFlags)flags;\n        window->ChildFlags = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0;\n        window->LastFrameActive = current_frame;\n        window->LastTimeActive = (float)g.Time;\n        window->BeginOrderWithinParent = 0;\n        window->BeginOrderWithinContext = (short)(g.WindowsActiveCount++);\n    }\n    else\n    {\n        flags = window->Flags;\n    }\n\n    // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack\n    ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window;\n    ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;\n    IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));\n\n    // We allow window memory to be compacted so recreate the base stack when needed.\n    if (window->IDStack.Size == 0)\n        window->IDStack.push_back(window->ID);\n\n    // Add to stack\n    g.CurrentWindow = window;\n    ImGuiWindowStackData window_stack_data;\n    window_stack_data.Window = window;\n    window_stack_data.ParentLastItemDataBackup = g.LastItemData;\n    window_stack_data.StackSizesOnBegin.SetToContextState(&g);\n    g.CurrentWindowStack.push_back(window_stack_data);\n    if (flags & ImGuiWindowFlags_ChildMenu)\n        g.BeginMenuCount++;\n\n    // Update ->RootWindow and others pointers (before any possible call to FocusWindow)\n    if (first_begin_of_the_frame)\n    {\n        UpdateWindowParentAndRootLinks(window, flags, parent_window);\n        window->ParentWindowInBeginStack = parent_window_in_stack;\n    }\n\n    // Add to focus scope stack\n    // We intentionally set g.CurrentWindow to NULL to prevent usage until when the viewport is set, then will call SetCurrentWindow()\n    PushFocusScope(window->ID);\n    window->NavRootFocusScopeId = g.CurrentFocusScopeId;\n    g.CurrentWindow = NULL;\n\n    // Add to popup stack\n    if (flags & ImGuiWindowFlags_Popup)\n    {\n        ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size];\n        popup_ref.Window = window;\n        popup_ref.ParentNavLayer = parent_window_in_stack->DC.NavLayerCurrent;\n        g.BeginPopupStack.push_back(popup_ref);\n        window->PopupId = popup_ref.PopupId;\n    }\n\n    // Process SetNextWindow***() calls\n    // (FIXME: Consider splitting the HasXXX flags into X/Y components\n    bool window_pos_set_by_api = false;\n    bool window_size_x_set_by_api = false, window_size_y_set_by_api = false;\n    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos)\n    {\n        window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0;\n        if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f)\n        {\n            // May be processed on the next frame if this is our first frame and we are measuring size\n            // FIXME: Look into removing the branch so everything can go through this same code path for consistency.\n            window->SetWindowPosVal = g.NextWindowData.PosVal;\n            window->SetWindowPosPivot = g.NextWindowData.PosPivotVal;\n            window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);\n        }\n        else\n        {\n            SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond);\n        }\n    }\n    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize)\n    {\n        window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f);\n        window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f);\n        if ((window->ChildFlags & ImGuiChildFlags_ResizeX) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0) // Axis-specific conditions for BeginChild()\n            g.NextWindowData.SizeVal.x = window->SizeFull.x;\n        if ((window->ChildFlags & ImGuiChildFlags_ResizeY) && (window->SetWindowSizeAllowFlags & ImGuiCond_FirstUseEver) == 0)\n            g.NextWindowData.SizeVal.y = window->SizeFull.y;\n        SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond);\n    }\n    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasScroll)\n    {\n        if (g.NextWindowData.ScrollVal.x >= 0.0f)\n        {\n            window->ScrollTarget.x = g.NextWindowData.ScrollVal.x;\n            window->ScrollTargetCenterRatio.x = 0.0f;\n        }\n        if (g.NextWindowData.ScrollVal.y >= 0.0f)\n        {\n            window->ScrollTarget.y = g.NextWindowData.ScrollVal.y;\n            window->ScrollTargetCenterRatio.y = 0.0f;\n        }\n    }\n    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasContentSize)\n        window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal;\n    else if (first_begin_of_the_frame)\n        window->ContentSizeExplicit = ImVec2(0.0f, 0.0f);\n    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed)\n        SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond);\n    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus)\n        FocusWindow(window);\n    if (window->Appearing)\n        SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false);\n\n    // When reusing window again multiple times a frame, just append content (don't need to setup again)\n    if (first_begin_of_the_frame)\n    {\n        // Initialize\n        const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345)\n        const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesCannotSkipItems > 0);\n        window->Active = true;\n        window->HasCloseButton = (p_open != NULL);\n        window->ClipRect = ImVec4(-FLT_MAX, -FLT_MAX, +FLT_MAX, +FLT_MAX);\n        window->IDStack.resize(1);\n        window->DrawList->_ResetForNewFrame();\n        window->DC.CurrentTableIdx = -1;\n\n        // Restore buffer capacity when woken from a compacted state, to avoid\n        if (window->MemoryCompacted)\n            GcAwakeTransientWindowBuffers(window);\n\n        // Update stored window name when it changes (which can _only_ happen with the \"###\" operator, so the ID would stay unchanged).\n        // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere.\n        bool window_title_visible_elsewhere = false;\n        if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0)   // Window titles visible when using CTRL+TAB\n            window_title_visible_elsewhere = true;\n        if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0)\n        {\n            size_t buf_len = (size_t)window->NameBufLen;\n            window->Name = ImStrdupcpy(window->Name, &buf_len, name);\n            window->NameBufLen = (int)buf_len;\n        }\n\n        // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS\n\n        // Update contents size from last frame for auto-fitting (or use explicit size)\n        CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal);\n        if (window->HiddenFramesCanSkipItems > 0)\n            window->HiddenFramesCanSkipItems--;\n        if (window->HiddenFramesCannotSkipItems > 0)\n            window->HiddenFramesCannotSkipItems--;\n        if (window->HiddenFramesForRenderOnly > 0)\n            window->HiddenFramesForRenderOnly--;\n\n        // Hide new windows for one frame until they calculate their size\n        if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api))\n            window->HiddenFramesCannotSkipItems = 1;\n\n        // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows)\n        // We reset Size/ContentSize for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size.\n        if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0)\n        {\n            window->HiddenFramesCannotSkipItems = 1;\n            if (flags & ImGuiWindowFlags_AlwaysAutoResize)\n            {\n                if (!window_size_x_set_by_api)\n                    window->Size.x = window->SizeFull.x = 0.f;\n                if (!window_size_y_set_by_api)\n                    window->Size.y = window->SizeFull.y = 0.f;\n                window->ContentSize = window->ContentSizeIdeal = ImVec2(0.f, 0.f);\n            }\n        }\n\n        // SELECT VIEWPORT\n        // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style)\n\n        ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();\n        SetWindowViewport(window, viewport);\n        SetCurrentWindow(window);\n\n        // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies)\n\n        if (flags & ImGuiWindowFlags_ChildWindow)\n            window->WindowBorderSize = style.ChildBorderSize;\n        else\n            window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;\n        window->WindowPadding = style.WindowPadding;\n        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f)\n            window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);\n\n        // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size.\n        window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);\n        window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;\n\n        bool use_current_size_for_scrollbar_x = window_just_created;\n        bool use_current_size_for_scrollbar_y = window_just_created;\n\n        // Collapse window by double-clicking on title bar\n        // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing\n        if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))\n        {\n            // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar.\n            ImRect title_bar_rect = window->TitleBarRect();\n            if (g.HoveredWindow == window && g.HoveredId == 0 && g.HoveredIdPreviousFrame == 0 && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseClickedCount[0] == 2)\n                window->WantCollapseToggle = true;\n            if (window->WantCollapseToggle)\n            {\n                window->Collapsed = !window->Collapsed;\n                if (!window->Collapsed)\n                    use_current_size_for_scrollbar_y = true;\n                MarkIniSettingsDirty(window);\n            }\n        }\n        else\n        {\n            window->Collapsed = false;\n        }\n        window->WantCollapseToggle = false;\n\n        // SIZE\n\n        // Outer Decoration Sizes\n        // (we need to clear ScrollbarSize immediatly as CalcWindowAutoFitSize() needs it and can be called from other locations).\n        const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes;\n        window->DecoOuterSizeX1 = 0.0f;\n        window->DecoOuterSizeX2 = 0.0f;\n        window->DecoOuterSizeY1 = window->TitleBarHeight() + window->MenuBarHeight();\n        window->DecoOuterSizeY2 = 0.0f;\n        window->ScrollbarSizes = ImVec2(0.0f, 0.0f);\n\n        // Calculate auto-fit size, handle automatic resize\n        const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal);\n        if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed)\n        {\n            // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc.\n            if (!window_size_x_set_by_api)\n            {\n                window->SizeFull.x = size_auto_fit.x;\n                use_current_size_for_scrollbar_x = true;\n            }\n            if (!window_size_y_set_by_api)\n            {\n                window->SizeFull.y = size_auto_fit.y;\n                use_current_size_for_scrollbar_y = true;\n            }\n        }\n        else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)\n        {\n            // Auto-fit may only grow window during the first few frames\n            // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed.\n            if (!window_size_x_set_by_api && window->AutoFitFramesX > 0)\n            {\n                window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x;\n                use_current_size_for_scrollbar_x = true;\n            }\n            if (!window_size_y_set_by_api && window->AutoFitFramesY > 0)\n            {\n                window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y;\n                use_current_size_for_scrollbar_y = true;\n            }\n            if (!window->Collapsed)\n                MarkIniSettingsDirty(window);\n        }\n\n        // Apply minimum/maximum window size constraints and final size\n        window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull);\n        window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;\n\n        // POSITION\n\n        // Popup latch its initial position, will position itself when it appears next frame\n        if (window_just_activated_by_user)\n        {\n            window->AutoPosLastDirection = ImGuiDir_None;\n            if ((flags & ImGuiWindowFlags_Popup) != 0 && !(flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api) // FIXME: BeginPopup() could use SetNextWindowPos()\n                window->Pos = g.BeginPopupStack.back().OpenPopupPos;\n        }\n\n        // Position child window\n        if (flags & ImGuiWindowFlags_ChildWindow)\n        {\n            IM_ASSERT(parent_window && parent_window->Active);\n            window->BeginOrderWithinParent = (short)parent_window->DC.ChildWindows.Size;\n            parent_window->DC.ChildWindows.push_back(window);\n            if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip)\n                window->Pos = parent_window->DC.CursorPos;\n        }\n\n        const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFramesCannotSkipItems == 0);\n        if (window_pos_with_pivot)\n            SetWindowPos(window, window->SetWindowPosVal - window->Size * window->SetWindowPosPivot, 0); // Position given a pivot (e.g. for centering)\n        else if ((flags & ImGuiWindowFlags_ChildMenu) != 0)\n            window->Pos = FindBestWindowPosForPopup(window);\n        else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize)\n            window->Pos = FindBestWindowPosForPopup(window);\n        else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip)\n            window->Pos = FindBestWindowPosForPopup(window);\n\n        // Calculate the range of allowed position for that window (to be movable and visible past safe area padding)\n        // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect.\n        ImRect viewport_rect(viewport->GetMainRect());\n        ImRect viewport_work_rect(viewport->GetWorkRect());\n        ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);\n        ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding);\n\n        // Clamp position/size so window stays visible within its viewport or monitor\n        // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.\n        if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow))\n            if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f)\n                ClampWindowPos(window, visibility_rect);\n        window->Pos = ImTrunc(window->Pos);\n\n        // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies)\n        // Large values tend to lead to variety of artifacts and are not recommended.\n        window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding;\n\n        // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts.\n        //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar))\n        //    window->WindowRounding = ImMin(window->WindowRounding, g.FontSize + style.FramePadding.y * 2.0f);\n\n        // Apply window focus (new and reactivated windows are moved to front)\n        bool want_focus = false;\n        if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))\n        {\n            if (flags & ImGuiWindowFlags_Popup)\n                want_focus = true;\n            else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0)\n                want_focus = true;\n        }\n\n        // [Test Engine] Register whole window in the item system (before submitting further decorations)\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n        if (g.TestEngineHookItems)\n        {\n            IM_ASSERT(window->IDStack.Size == 1);\n            window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself.\n            IMGUI_TEST_ENGINE_ITEM_ADD(window->ID, window->Rect(), NULL);\n            IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0);\n            window->IDStack.Size = 1;\n        }\n#endif\n\n        // Handle manual resize: Resize Grips, Borders, Gamepad\n        int border_hovered = -1, border_held = -1;\n        ImU32 resize_grip_col[4] = {};\n        const int resize_grip_count = (window->Flags & ImGuiWindowFlags_ChildWindow) ? 0 : g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it.\n        const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f));\n        if (!window->Collapsed)\n            if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect))\n            {\n                if (auto_fit_mask & (1 << ImGuiAxis_X))\n                    use_current_size_for_scrollbar_x = true;\n                if (auto_fit_mask & (1 << ImGuiAxis_Y))\n                    use_current_size_for_scrollbar_y = true;\n            }\n        window->ResizeBorderHovered = (signed char)border_hovered;\n        window->ResizeBorderHeld = (signed char)border_held;\n\n        // SCROLLBAR VISIBILITY\n\n        // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size).\n        if (!window->Collapsed)\n        {\n            // When reading the current size we need to read it after size constraints have been applied.\n            // Intentionally use previous frame values for InnerRect and ScrollbarSizes.\n            // And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet.\n            ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2));\n            ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame;\n            ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f;\n            float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x;\n            float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y;\n            //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons?\n            window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar));\n            window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar));\n            if (window->ScrollbarX && !window->ScrollbarY)\n                window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar);\n            window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f);\n\n            // Amend the partially filled window->DecorationXXX values.\n            window->DecoOuterSizeX2 += window->ScrollbarSizes.x;\n            window->DecoOuterSizeY2 += window->ScrollbarSizes.y;\n        }\n\n        // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING)\n        // Update various regions. Variables they depend on should be set above in this function.\n        // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame.\n\n        // Outer rectangle\n        // Not affected by window border size. Used by:\n        // - FindHoveredWindow() (w/ extra padding when border resize is enabled)\n        // - Begin() initial clipping rect for drawing window background and borders.\n        // - Begin() clipping whole child\n        const ImRect host_rect = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) ? parent_window->ClipRect : viewport_rect;\n        const ImRect outer_rect = window->Rect();\n        const ImRect title_bar_rect = window->TitleBarRect();\n        window->OuterRectClipped = outer_rect;\n        window->OuterRectClipped.ClipWith(host_rect);\n\n        // Inner rectangle\n        // Not affected by window border size. Used by:\n        // - InnerClipRect\n        // - ScrollToRectEx()\n        // - NavUpdatePageUpPageDown()\n        // - Scrollbar()\n        window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1;\n        window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1;\n        window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2;\n        window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2;\n\n        // Inner clipping rectangle.\n        // Will extend a little bit outside the normal work region.\n        // This is to allow e.g. Selectable or CollapsingHeader or some separators to cover that space.\n        // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result.\n        // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior.\n        // Affected by window/frame border size. Used by:\n        // - Begin() initial clip rect\n        float top_border_size = (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize);\n        window->InnerClipRect.Min.x = ImTrunc(0.5f + window->InnerRect.Min.x + ImMax(ImTrunc(window->WindowPadding.x * 0.5f), window->WindowBorderSize));\n        window->InnerClipRect.Min.y = ImTrunc(0.5f + window->InnerRect.Min.y + top_border_size);\n        window->InnerClipRect.Max.x = ImTrunc(0.5f + window->InnerRect.Max.x - ImMax(ImTrunc(window->WindowPadding.x * 0.5f), window->WindowBorderSize));\n        window->InnerClipRect.Max.y = ImTrunc(0.5f + window->InnerRect.Max.y - window->WindowBorderSize);\n        window->InnerClipRect.ClipWithFull(host_rect);\n\n        // Default item width. Make it proportional to window size if window manually resizes\n        if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize))\n            window->ItemWidthDefault = ImTrunc(window->Size.x * 0.65f);\n        else\n            window->ItemWidthDefault = ImTrunc(g.FontSize * 16.0f);\n\n        // SCROLLING\n\n        // Lock down maximum scrolling\n        // The value of ScrollMax are ahead from ScrollbarX/ScrollbarY which is intentionally using InnerRect from previous rect in order to accommodate\n        // for right/bottom aligned items without creating a scrollbar.\n        window->ScrollMax.x = ImMax(0.0f, window->ContentSize.x + window->WindowPadding.x * 2.0f - window->InnerRect.GetWidth());\n        window->ScrollMax.y = ImMax(0.0f, window->ContentSize.y + window->WindowPadding.y * 2.0f - window->InnerRect.GetHeight());\n\n        // Apply scrolling\n        window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window);\n        window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX);\n        window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f;\n\n        // DRAWING\n\n        // Setup draw list and outer clipping rectangle\n        IM_ASSERT(window->DrawList->CmdBuffer.Size == 1 && window->DrawList->CmdBuffer[0].ElemCount == 0);\n        window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID);\n        PushClipRect(host_rect.Min, host_rect.Max, false);\n\n        // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71)\n        // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order.\n        // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493)\n        {\n            bool render_decorations_in_parent = false;\n            if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip)\n            {\n                // - We test overlap with the previous child window only (testing all would end up being O(log N) not a good investment here)\n                // - We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping childs\n                ImGuiWindow* previous_child = parent_window->DC.ChildWindows.Size >= 2 ? parent_window->DC.ChildWindows[parent_window->DC.ChildWindows.Size - 2] : NULL;\n                bool previous_child_overlapping = previous_child ? previous_child->Rect().Overlaps(window->Rect()) : false;\n                bool parent_is_empty = (parent_window->DrawList->VtxBuffer.Size == 0);\n                if (window->DrawList->CmdBuffer.back().ElemCount == 0 && !parent_is_empty && !previous_child_overlapping)\n                    render_decorations_in_parent = true;\n            }\n            if (render_decorations_in_parent)\n                window->DrawList = parent_window->DrawList;\n\n            // Handle title bar, scrollbar, resize grips and resize borders\n            const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;\n            const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);\n            const bool handle_borders_and_resize_grips = true; // This exists to facilitate merge with 'docking' branch.\n            RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size);\n\n            if (render_decorations_in_parent)\n                window->DrawList = &window->DrawListInst;\n        }\n\n        // UPDATE RECTANGLES (2- THOSE AFFECTED BY SCROLLING)\n\n        // Work rectangle.\n        // Affected by window padding and border size. Used by:\n        // - Columns() for right-most edge\n        // - TreeNode(), CollapsingHeader() for right-most edge\n        // - BeginTabBar() for right-most edge\n        const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar);\n        const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar);\n        const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));\n        const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));\n        window->WorkRect.Min.x = ImTrunc(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize));\n        window->WorkRect.Min.y = ImTrunc(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize));\n        window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x;\n        window->WorkRect.Max.y = window->WorkRect.Min.y + work_rect_size_y;\n        window->ParentWorkRect = window->WorkRect;\n\n        // [LEGACY] Content Region\n        // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it.\n        // Unless explicit content size is specified by user, this currently represent the region leading to no scrolling.\n        // Used by:\n        // - Mouse wheel scrolling + many other things\n        window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1;\n        window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1;\n        window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2)));\n        window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)));\n\n        // Setup drawing context\n        // (NB: That term \"drawing context / DC\" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.)\n        window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x;\n        window->DC.GroupOffset.x = 0.0f;\n        window->DC.ColumnsOffset.x = 0.0f;\n\n        // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount.\n        // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64.\n        double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x;\n        double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1;\n        window->DC.CursorStartPos  = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y);\n        window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y));\n        window->DC.CursorPos = window->DC.CursorStartPos;\n        window->DC.CursorPosPrevLine = window->DC.CursorPos;\n        window->DC.CursorMaxPos = window->DC.CursorStartPos;\n        window->DC.IdealMaxPos = window->DC.CursorStartPos;\n        window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f);\n        window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f;\n        window->DC.IsSameLine = window->DC.IsSetPos = false;\n\n        window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n        window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext;\n        window->DC.NavLayersActiveMaskNext = 0x00;\n        window->DC.NavIsScrollPushableX = true;\n        window->DC.NavHideHighlightOneFrame = false;\n        window->DC.NavWindowHasScrollY = (window->ScrollMax.y > 0.0f);\n\n        window->DC.MenuBarAppending = false;\n        window->DC.MenuColumns.Update(style.ItemSpacing.x, window_just_activated_by_user);\n        window->DC.TreeDepth = 0;\n        window->DC.TreeJumpToParentOnPopMask = 0x00;\n        window->DC.ChildWindows.resize(0);\n        window->DC.StateStorage = &window->StateStorage;\n        window->DC.CurrentColumns = NULL;\n        window->DC.LayoutType = ImGuiLayoutType_Vertical;\n        window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical;\n\n        window->DC.ItemWidth = window->ItemWidthDefault;\n        window->DC.TextWrapPos = -1.0f; // disabled\n        window->DC.ItemWidthStack.resize(0);\n        window->DC.TextWrapPosStack.resize(0);\n\n        if (window->AutoFitFramesX > 0)\n            window->AutoFitFramesX--;\n        if (window->AutoFitFramesY > 0)\n            window->AutoFitFramesY--;\n\n        // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there)\n        // We ImGuiFocusRequestFlags_UnlessBelowModal to:\n        // - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed.\n        // - Position window behind the modal that is not a begin-parent of this window.\n        if (want_focus)\n            FocusWindow(window, ImGuiFocusRequestFlags_UnlessBelowModal);\n        if (want_focus && window == g.NavWindow)\n            NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls\n\n        // Title bar\n        if (!(flags & ImGuiWindowFlags_NoTitleBar))\n            RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open);\n\n        // Clear hit test shape every frame\n        window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;\n\n        // Pressing CTRL+C while holding on a window copy its content to the clipboard\n        // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope.\n        // Maybe we can support CTRL+C on every element?\n        /*\n        //if (g.NavWindow == window && g.ActiveId == 0)\n        if (g.ActiveId == window->MoveId)\n            if (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_C))\n                LogToClipboard();\n        */\n\n        // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().\n        // This is useful to allow creating context menus on title bar only, etc.\n        SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect);\n\n        // [DEBUG]\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n        if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId))\n            DebugLocateItemResolveWithLastItem();\n#endif\n\n        // [Test Engine] Register title bar / tab with MoveId.\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n        if (!(window->Flags & ImGuiWindowFlags_NoTitleBar))\n            IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.ID, g.LastItemData.Rect, &g.LastItemData);\n#endif\n    }\n    else\n    {\n        // Append\n        SetCurrentWindow(window);\n    }\n\n    PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);\n\n    // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default \"Debug\" window is unused)\n    window->WriteAccessed = false;\n    window->BeginCount++;\n    g.NextWindowData.ClearFlags();\n\n    // Update visibility\n    if (first_begin_of_the_frame)\n    {\n        if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu))\n        {\n            // Child window can be out of sight and have \"negative\" clip windows.\n            // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).\n            IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);\n            const bool nav_request = (flags & ImGuiWindowFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav);\n            if (!g.LogEnabled && !nav_request)\n                if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)\n                {\n                    if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0)\n                        window->HiddenFramesCannotSkipItems = 1;\n                    else\n                        window->HiddenFramesCanSkipItems = 1;\n                }\n\n            // Hide along with parent or if parent is collapsed\n            if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCanSkipItems > 0))\n                window->HiddenFramesCanSkipItems = 1;\n            if (parent_window && (parent_window->Collapsed || parent_window->HiddenFramesCannotSkipItems > 0))\n                window->HiddenFramesCannotSkipItems = 1;\n        }\n\n        // Don't render if style alpha is 0.0 at the time of Begin(). This is arbitrary and inconsistent but has been there for a long while (may remove at some point)\n        if (style.Alpha <= 0.0f)\n            window->HiddenFramesCanSkipItems = 1;\n\n        // Update the Hidden flag\n        bool hidden_regular = (window->HiddenFramesCanSkipItems > 0) || (window->HiddenFramesCannotSkipItems > 0);\n        window->Hidden = hidden_regular || (window->HiddenFramesForRenderOnly > 0);\n\n        // Disable inputs for requested number of frames\n        if (window->DisableInputsFrames > 0)\n        {\n            window->DisableInputsFrames--;\n            window->Flags |= ImGuiWindowFlags_NoInputs;\n        }\n\n        // Update the SkipItems flag, used to early out of all items functions (no layout required)\n        bool skip_items = false;\n        if (window->Collapsed || !window->Active || hidden_regular)\n            if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0)\n                skip_items = true;\n        window->SkipItems = skip_items;\n    }\n\n    // [DEBUG] io.ConfigDebugBeginReturnValue override return value to test Begin/End and BeginChild/EndChild behaviors.\n    // (The implicit fallback window is NOT automatically ended allowing it to always be able to receive commands without crashing)\n    if (!window->IsFallbackWindow && ((g.IO.ConfigDebugBeginReturnValueOnce && window_just_created) || (g.IO.ConfigDebugBeginReturnValueLoop && g.DebugBeginReturnValueCullDepth == g.CurrentWindowStack.Size)))\n    {\n        if (window->AutoFitFramesX > 0) { window->AutoFitFramesX++; }\n        if (window->AutoFitFramesY > 0) { window->AutoFitFramesY++; }\n        return false;\n    }\n\n    return !window->SkipItems;\n}\n\nvoid ImGui::End()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Error checking: verify that user hasn't called End() too many times!\n    if (g.CurrentWindowStack.Size <= 1 && g.WithinFrameScopeWithImplicitWindow)\n    {\n        IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size > 1, \"Calling End() too many times!\");\n        return;\n    }\n    IM_ASSERT(g.CurrentWindowStack.Size > 0);\n\n    // Error checking: verify that user doesn't directly call End() on a child window.\n    if (window->Flags & ImGuiWindowFlags_ChildWindow)\n        IM_ASSERT_USER_ERROR(g.WithinEndChild, \"Must call EndChild() and not End()!\");\n\n    // Close anything that is open\n    if (window->DC.CurrentColumns)\n        EndColumns();\n    PopClipRect();   // Inner window clip rectangle\n    PopFocusScope();\n\n    // Stop logging\n    if (!(window->Flags & ImGuiWindowFlags_ChildWindow))    // FIXME: add more options for scope of logging\n        LogFinish();\n\n    if (window->DC.IsSetPos)\n        ErrorCheckUsingSetCursorPosToExtendParentBoundaries();\n\n    // Pop from window stack\n    g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup;\n    if (window->Flags & ImGuiWindowFlags_ChildMenu)\n        g.BeginMenuCount--;\n    if (window->Flags & ImGuiWindowFlags_Popup)\n        g.BeginPopupStack.pop_back();\n    g.CurrentWindowStack.back().StackSizesOnBegin.CompareWithContextState(&g);\n    g.CurrentWindowStack.pop_back();\n    SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window);\n}\n\nvoid ImGui::BringWindowToFocusFront(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(window == window->RootWindow);\n\n    const int cur_order = window->FocusOrder;\n    IM_ASSERT(g.WindowsFocusOrder[cur_order] == window);\n    if (g.WindowsFocusOrder.back() == window)\n        return;\n\n    const int new_order = g.WindowsFocusOrder.Size - 1;\n    for (int n = cur_order; n < new_order; n++)\n    {\n        g.WindowsFocusOrder[n] = g.WindowsFocusOrder[n + 1];\n        g.WindowsFocusOrder[n]->FocusOrder--;\n        IM_ASSERT(g.WindowsFocusOrder[n]->FocusOrder == n);\n    }\n    g.WindowsFocusOrder[new_order] = window;\n    window->FocusOrder = (short)new_order;\n}\n\nvoid ImGui::BringWindowToDisplayFront(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* current_front_window = g.Windows.back();\n    if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better)\n        return;\n    for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window\n        if (g.Windows[i] == window)\n        {\n            memmove(&g.Windows[i], &g.Windows[i + 1], (size_t)(g.Windows.Size - i - 1) * sizeof(ImGuiWindow*));\n            g.Windows[g.Windows.Size - 1] = window;\n            break;\n        }\n}\n\nvoid ImGui::BringWindowToDisplayBack(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.Windows[0] == window)\n        return;\n    for (int i = 0; i < g.Windows.Size; i++)\n        if (g.Windows[i] == window)\n        {\n            memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*));\n            g.Windows[0] = window;\n            break;\n        }\n}\n\nvoid ImGui::BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* behind_window)\n{\n    IM_ASSERT(window != NULL && behind_window != NULL);\n    ImGuiContext& g = *GImGui;\n    window = window->RootWindow;\n    behind_window = behind_window->RootWindow;\n    int pos_wnd = FindWindowDisplayIndex(window);\n    int pos_beh = FindWindowDisplayIndex(behind_window);\n    if (pos_wnd < pos_beh)\n    {\n        size_t copy_bytes = (pos_beh - pos_wnd - 1) * sizeof(ImGuiWindow*);\n        memmove(&g.Windows.Data[pos_wnd], &g.Windows.Data[pos_wnd + 1], copy_bytes);\n        g.Windows[pos_beh - 1] = window;\n    }\n    else\n    {\n        size_t copy_bytes = (pos_wnd - pos_beh) * sizeof(ImGuiWindow*);\n        memmove(&g.Windows.Data[pos_beh + 1], &g.Windows.Data[pos_beh], copy_bytes);\n        g.Windows[pos_beh] = window;\n    }\n}\n\nint ImGui::FindWindowDisplayIndex(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    return g.Windows.index_from_ptr(g.Windows.find(window));\n}\n\n// Moving window to front of display and set focus (which happens to be back of our sorted list)\nvoid ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Modal check?\n    if ((flags & ImGuiFocusRequestFlags_UnlessBelowModal) && (g.NavWindow != window)) // Early out in common case.\n        if (ImGuiWindow* blocking_modal = FindBlockingModal(window))\n        {\n            IMGUI_DEBUG_LOG_FOCUS(\"[focus] FocusWindow(\\\"%s\\\", UnlessBelowModal): prevented by \\\"%s\\\".\\n\", window ? window->Name : \"<NULL>\", blocking_modal->Name);\n            if (window && window == window->RootWindow && (window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)\n                BringWindowToDisplayBehind(window, blocking_modal); // Still bring to right below modal.\n            return;\n        }\n\n    // Find last focused child (if any) and focus it instead.\n    if ((flags & ImGuiFocusRequestFlags_RestoreFocusedChild) && window != NULL)\n        window = NavRestoreLastChildNavWindow(window);\n\n    // Apply focus\n    if (g.NavWindow != window)\n    {\n        SetNavWindow(window);\n        if (window && g.NavDisableMouseHover)\n            g.NavMousePosDirty = true;\n        g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId\n        g.NavLayer = ImGuiNavLayer_Main;\n        g.NavFocusScopeId = window ? window->NavRootFocusScopeId : 0;\n        g.NavIdIsAlive = false;\n        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;\n\n        // Close popups if any\n        ClosePopupsOverWindow(window, false);\n    }\n\n    // Move the root window to the top of the pile\n    IM_ASSERT(window == NULL || window->RootWindow != NULL);\n    ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop\n    ImGuiWindow* display_front_window = window ? window->RootWindow : NULL;\n\n    // Steal active widgets. Some of the cases it triggers includes:\n    // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run.\n    // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId)\n    if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window)\n        if (!g.ActiveIdNoClearOnFocusLoss)\n            ClearActiveID();\n\n    // Passing NULL allow to disable keyboard focus\n    if (!window)\n        return;\n\n    // Bring to front\n    BringWindowToFocusFront(focus_front_window);\n    if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0)\n        BringWindowToDisplayFront(display_front_window);\n}\n\nvoid ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    IM_UNUSED(filter_viewport); // Unused in master branch.\n    int start_idx = g.WindowsFocusOrder.Size - 1;\n    if (under_this_window != NULL)\n    {\n        // Aim at root window behind us, if we are in a child window that's our own root (see #4640)\n        int offset = -1;\n        while (under_this_window->Flags & ImGuiWindowFlags_ChildWindow)\n        {\n            under_this_window = under_this_window->ParentWindow;\n            offset = 0;\n        }\n        start_idx = FindWindowFocusIndex(under_this_window) + offset;\n    }\n    for (int i = start_idx; i >= 0; i--)\n    {\n        // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user.\n        ImGuiWindow* window = g.WindowsFocusOrder[i];\n        if (window == ignore_window || !window->WasActive)\n            continue;\n        if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs))\n        {\n            FocusWindow(window, flags);\n            return;\n        }\n    }\n    FocusWindow(NULL, flags);\n}\n\n// Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only.\nvoid ImGui::SetCurrentFont(ImFont* font)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(font && font->IsLoaded());    // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ?\n    IM_ASSERT(font->Scale > 0.0f);\n    g.Font = font;\n    g.FontBaseSize = ImMax(1.0f, g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale);\n    g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f;\n\n    ImFontAtlas* atlas = g.Font->ContainerAtlas;\n    g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel;\n    g.DrawListSharedData.TexUvLines = atlas->TexUvLines;\n    g.DrawListSharedData.Font = g.Font;\n    g.DrawListSharedData.FontSize = g.FontSize;\n}\n\nvoid ImGui::PushFont(ImFont* font)\n{\n    ImGuiContext& g = *GImGui;\n    if (!font)\n        font = GetDefaultFont();\n    SetCurrentFont(font);\n    g.FontStack.push_back(font);\n    g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID);\n}\n\nvoid  ImGui::PopFont()\n{\n    ImGuiContext& g = *GImGui;\n    g.CurrentWindow->DrawList->PopTextureID();\n    g.FontStack.pop_back();\n    SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back());\n}\n\nvoid ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiItemFlags item_flags = g.CurrentItemFlags;\n    IM_ASSERT(item_flags == g.ItemFlagsStack.back());\n    if (enabled)\n        item_flags |= option;\n    else\n        item_flags &= ~option;\n    g.CurrentItemFlags = item_flags;\n    g.ItemFlagsStack.push_back(item_flags);\n}\n\nvoid ImGui::PopItemFlag()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack.\n    g.ItemFlagsStack.pop_back();\n    g.CurrentItemFlags = g.ItemFlagsStack.back();\n}\n\n// BeginDisabled()/EndDisabled()\n// - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)\n// - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently.\n// - Feedback welcome at https://github.com/ocornut/imgui/issues/211\n// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.\n// - Optimized shortcuts instead of PushStyleVar() + PushItemFlag()\nvoid ImGui::BeginDisabled(bool disabled)\n{\n    ImGuiContext& g = *GImGui;\n    bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;\n    if (!was_disabled && disabled)\n    {\n        g.DisabledAlphaBackup = g.Style.Alpha;\n        g.Style.Alpha *= g.Style.DisabledAlpha; // PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * g.Style.DisabledAlpha);\n    }\n    if (was_disabled || disabled)\n        g.CurrentItemFlags |= ImGuiItemFlags_Disabled;\n    g.ItemFlagsStack.push_back(g.CurrentItemFlags);\n    g.DisabledStackSize++;\n}\n\nvoid ImGui::EndDisabled()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.DisabledStackSize > 0);\n    g.DisabledStackSize--;\n    bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;\n    //PopItemFlag();\n    g.ItemFlagsStack.pop_back();\n    g.CurrentItemFlags = g.ItemFlagsStack.back();\n    if (was_disabled && (g.CurrentItemFlags & ImGuiItemFlags_Disabled) == 0)\n        g.Style.Alpha = g.DisabledAlphaBackup; //PopStyleVar();\n}\n\nvoid ImGui::PushTabStop(bool tab_stop)\n{\n    PushItemFlag(ImGuiItemFlags_NoTabStop, !tab_stop);\n}\n\nvoid ImGui::PopTabStop()\n{\n    PopItemFlag();\n}\n\nvoid ImGui::PushButtonRepeat(bool repeat)\n{\n    PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat);\n}\n\nvoid ImGui::PopButtonRepeat()\n{\n    PopItemFlag();\n}\n\nvoid ImGui::PushTextWrapPos(float wrap_pos_x)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos);\n    window->DC.TextWrapPos = wrap_pos_x;\n}\n\nvoid ImGui::PopTextWrapPos()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.TextWrapPos = window->DC.TextWrapPosStack.back();\n    window->DC.TextWrapPosStack.pop_back();\n}\n\nstatic ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy)\n{\n    ImGuiWindow* last_window = NULL;\n    while (last_window != window)\n    {\n        last_window = window;\n        window = window->RootWindow;\n        if (popup_hierarchy)\n            window = window->RootWindowPopupTree;\n    }\n    return window;\n}\n\nbool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy)\n{\n    ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy);\n    if (window_root == potential_parent)\n        return true;\n    while (window != NULL)\n    {\n        if (window == potential_parent)\n            return true;\n        if (window == window_root) // end of chain\n            return false;\n        window = window->ParentWindow;\n    }\n    return false;\n}\n\nbool ImGui::IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent)\n{\n    if (window->RootWindow == potential_parent)\n        return true;\n    while (window != NULL)\n    {\n        if (window == potential_parent)\n            return true;\n        window = window->ParentWindowInBeginStack;\n    }\n    return false;\n}\n\nbool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below)\n{\n    ImGuiContext& g = *GImGui;\n\n    // It would be saner to ensure that display layer is always reflected in the g.Windows[] order, which would likely requires altering all manipulations of that array\n    const int display_layer_delta = GetWindowDisplayLayer(potential_above) - GetWindowDisplayLayer(potential_below);\n    if (display_layer_delta != 0)\n        return display_layer_delta > 0;\n\n    for (int i = g.Windows.Size - 1; i >= 0; i--)\n    {\n        ImGuiWindow* candidate_window = g.Windows[i];\n        if (candidate_window == potential_above)\n            return true;\n        if (candidate_window == potential_below)\n            return false;\n    }\n    return false;\n}\n\n// Is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options.\n// IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app,\n// you should not use this function! Use the 'io.WantCaptureMouse' boolean for that!\n// Refer to FAQ entry \"How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?\" for details.\nbool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)\n{\n    IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0 && \"Invalid flags for IsWindowHovered()!\");\n\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* ref_window = g.HoveredWindow;\n    ImGuiWindow* cur_window = g.CurrentWindow;\n    if (ref_window == NULL)\n        return false;\n\n    if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)\n    {\n        IM_ASSERT(cur_window); // Not inside a Begin()/End()\n        const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;\n        if (flags & ImGuiHoveredFlags_RootWindow)\n            cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);\n\n        bool result;\n        if (flags & ImGuiHoveredFlags_ChildWindows)\n            result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy);\n        else\n            result = (ref_window == cur_window);\n        if (!result)\n            return false;\n    }\n\n    if (!IsWindowContentHoverable(ref_window, flags))\n        return false;\n    if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))\n        if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)\n            return false;\n\n    // When changing hovered window we requires a bit of stationary delay before activating hover timer.\n    // FIXME: We don't support delay other than stationary one for now, other delay would need a way\n    // to fullfill the possibility that multiple IsWindowHovered() with varying flag could return true\n    // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.\n    // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.\n    if (flags & ImGuiHoveredFlags_ForTooltip)\n        flags = ApplyHoverFlagsForTooltip(flags, g.Style.HoverFlagsForTooltipMouse);\n    if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)\n        return false;\n\n    return true;\n}\n\nbool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* ref_window = g.NavWindow;\n    ImGuiWindow* cur_window = g.CurrentWindow;\n\n    if (ref_window == NULL)\n        return false;\n    if (flags & ImGuiFocusedFlags_AnyWindow)\n        return true;\n\n    IM_ASSERT(cur_window); // Not inside a Begin()/End()\n    const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;\n    if (flags & ImGuiHoveredFlags_RootWindow)\n        cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy);\n\n    if (flags & ImGuiHoveredFlags_ChildWindows)\n        return IsWindowChildOf(ref_window, cur_window, popup_hierarchy);\n    else\n        return (ref_window == cur_window);\n}\n\n// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)\n// Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically.\n// If you want a window to never be focused, you may use the e.g. NoInputs flag.\nbool ImGui::IsWindowNavFocusable(ImGuiWindow* window)\n{\n    return window->WasActive && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);\n}\n\nfloat ImGui::GetWindowWidth()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->Size.x;\n}\n\nfloat ImGui::GetWindowHeight()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->Size.y;\n}\n\nImVec2 ImGui::GetWindowPos()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    return window->Pos;\n}\n\nvoid ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond)\n{\n    // Test condition (NB: bit 0 is always true) and clear flags for next time\n    if (cond && (window->SetWindowPosAllowFlags & cond) == 0)\n        return;\n\n    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.\n    window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);\n    window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX);\n\n    // Set\n    const ImVec2 old_pos = window->Pos;\n    window->Pos = ImTrunc(pos);\n    ImVec2 offset = window->Pos - old_pos;\n    if (offset.x == 0.0f && offset.y == 0.0f)\n        return;\n    MarkIniSettingsDirty(window);\n    window->DC.CursorPos += offset;         // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor\n    window->DC.CursorMaxPos += offset;      // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected.\n    window->DC.IdealMaxPos += offset;\n    window->DC.CursorStartPos += offset;\n}\n\nvoid ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond)\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    SetWindowPos(window, pos, cond);\n}\n\nvoid ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond)\n{\n    if (ImGuiWindow* window = FindWindowByName(name))\n        SetWindowPos(window, pos, cond);\n}\n\nImVec2 ImGui::GetWindowSize()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->Size;\n}\n\nvoid ImGui::SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond)\n{\n    // Test condition (NB: bit 0 is always true) and clear flags for next time\n    if (cond && (window->SetWindowSizeAllowFlags & cond) == 0)\n        return;\n\n    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.\n    window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);\n\n    // Enable auto-fit (not done in BeginChild() path unless appearing or combined with ImGuiChildFlags_AlwaysAutoResize)\n    if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)\n        window->AutoFitFramesX = (size.x <= 0.0f) ? 2 : 0;\n    if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || window->Appearing || (window->ChildFlags & ImGuiChildFlags_AlwaysAutoResize) != 0)\n        window->AutoFitFramesY = (size.y <= 0.0f) ? 2 : 0;\n\n    // Set\n    ImVec2 old_size = window->SizeFull;\n    if (size.x <= 0.0f)\n        window->AutoFitOnlyGrows = false;\n    else\n        window->SizeFull.x = IM_TRUNC(size.x);\n    if (size.y <= 0.0f)\n        window->AutoFitOnlyGrows = false;\n    else\n        window->SizeFull.y = IM_TRUNC(size.y);\n    if (old_size.x != window->SizeFull.x || old_size.y != window->SizeFull.y)\n        MarkIniSettingsDirty(window);\n}\n\nvoid ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond)\n{\n    SetWindowSize(GImGui->CurrentWindow, size, cond);\n}\n\nvoid ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond)\n{\n    if (ImGuiWindow* window = FindWindowByName(name))\n        SetWindowSize(window, size, cond);\n}\n\nvoid ImGui::SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond)\n{\n    // Test condition (NB: bit 0 is always true) and clear flags for next time\n    if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0)\n        return;\n    window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing);\n\n    // Set\n    window->Collapsed = collapsed;\n}\n\nvoid ImGui::SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)\n{\n    IM_ASSERT(window->HitTestHoleSize.x == 0);     // We don't support multiple holes/hit test filters\n    window->HitTestHoleSize = ImVec2ih(size);\n    window->HitTestHoleOffset = ImVec2ih(pos - window->Pos);\n}\n\nvoid ImGui::SetWindowHiddendAndSkipItemsForCurrentFrame(ImGuiWindow* window)\n{\n    window->Hidden = window->SkipItems = true;\n    window->HiddenFramesCanSkipItems = 1;\n}\n\nvoid ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)\n{\n    SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);\n}\n\nbool ImGui::IsWindowCollapsed()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->Collapsed;\n}\n\nbool ImGui::IsWindowAppearing()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->Appearing;\n}\n\nvoid ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond)\n{\n    if (ImGuiWindow* window = FindWindowByName(name))\n        SetWindowCollapsed(window, collapsed, cond);\n}\n\nvoid ImGui::SetWindowFocus()\n{\n    FocusWindow(GImGui->CurrentWindow);\n}\n\nvoid ImGui::SetWindowFocus(const char* name)\n{\n    if (name)\n    {\n        if (ImGuiWindow* window = FindWindowByName(name))\n            FocusWindow(window);\n    }\n    else\n    {\n        FocusWindow(NULL);\n    }\n}\n\nvoid ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.\n    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasPos;\n    g.NextWindowData.PosVal = pos;\n    g.NextWindowData.PosPivotVal = pivot;\n    g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;\n}\n\nvoid ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.\n    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSize;\n    g.NextWindowData.SizeVal = size;\n    g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always;\n}\n\n// For each axis:\n// - Use 0.0f as min or FLT_MAX as max if you don't want limits, e.g. size_min = (500.0f, 0.0f), size_max = (FLT_MAX, FLT_MAX) sets a minimum width.\n// - Use -1 for both min and max of same axis to preserve current size which itself is a constraint.\n// - See \"Demo->Examples->Constrained-resizing window\" for examples.\nvoid ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasSizeConstraint;\n    g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max);\n    g.NextWindowData.SizeCallback = custom_callback;\n    g.NextWindowData.SizeCallbackUserData = custom_callback_user_data;\n}\n\n// Content size = inner scrollable rectangle, padded with WindowPadding.\n// SetNextWindowContentSize(ImVec2(100,100) + ImGuiWindowFlags_AlwaysAutoResize will always allow submitting a 100x100 item.\nvoid ImGui::SetNextWindowContentSize(const ImVec2& size)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasContentSize;\n    g.NextWindowData.ContentSizeVal = ImTrunc(size);\n}\n\nvoid ImGui::SetNextWindowScroll(const ImVec2& scroll)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasScroll;\n    g.NextWindowData.ScrollVal = scroll;\n}\n\nvoid ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags.\n    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasCollapsed;\n    g.NextWindowData.CollapsedVal = collapsed;\n    g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always;\n}\n\nvoid ImGui::SetNextWindowFocus()\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasFocus;\n}\n\nvoid ImGui::SetNextWindowBgAlpha(float alpha)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasBgAlpha;\n    g.NextWindowData.BgAlphaVal = alpha;\n}\n\nImDrawList* ImGui::GetWindowDrawList()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    return window->DrawList;\n}\n\nImFont* ImGui::GetFont()\n{\n    return GImGui->Font;\n}\n\nfloat ImGui::GetFontSize()\n{\n    return GImGui->FontSize;\n}\n\nImVec2 ImGui::GetFontTexUvWhitePixel()\n{\n    return GImGui->DrawListSharedData.TexUvWhitePixel;\n}\n\nvoid ImGui::SetWindowFontScale(float scale)\n{\n    IM_ASSERT(scale > 0.0f);\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    window->FontWindowScale = scale;\n    g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize();\n}\n\nvoid ImGui::PushFocusScope(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    g.FocusScopeStack.push_back(id);\n    g.CurrentFocusScopeId = id;\n}\n\nvoid ImGui::PopFocusScope()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ?\n    g.FocusScopeStack.pop_back();\n    g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back() : 0;\n}\n\n// Focus = move navigation cursor, set scrolling, set focus window.\nvoid ImGui::FocusItem()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IMGUI_DEBUG_LOG_FOCUS(\"FocusItem(0x%08x) in window \\\"%s\\\"\\n\", g.LastItemData.ID, window->Name);\n    if (g.DragDropActive || g.MovingWindow != NULL) // FIXME: Opt-in flags for this?\n    {\n        IMGUI_DEBUG_LOG_FOCUS(\"FocusItem() ignored while DragDropActive!\\n\");\n        return;\n    }\n\n    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight | ImGuiNavMoveFlags_NoSelect;\n    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;\n    SetNavWindow(window);\n    NavMoveRequestSubmit(ImGuiDir_None, ImGuiDir_Up, move_flags, scroll_flags);\n    NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);\n}\n\nvoid ImGui::ActivateItemByID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    g.NavNextActivateId = id;\n    g.NavNextActivateFlags = ImGuiActivateFlags_None;\n}\n\n// Note: this will likely be called ActivateItem() once we rework our Focus/Activation system!\n// But ActivateItem() should function without altering scroll/focus?\nvoid ImGui::SetKeyboardFocusHere(int offset)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(offset >= -1);    // -1 is allowed but not below\n    IMGUI_DEBUG_LOG_FOCUS(\"SetKeyboardFocusHere(%d) in window \\\"%s\\\"\\n\", offset, window->Name);\n\n    // It makes sense in the vast majority of cases to never interrupt a drag and drop.\n    // When we refactor this function into ActivateItem() we may want to make this an option.\n    // MovingWindow is protected from most user inputs using SetActiveIdUsingNavAndKeys(), but\n    // is also automatically dropped in the event g.ActiveId is stolen.\n    if (g.DragDropActive || g.MovingWindow != NULL)\n    {\n        IMGUI_DEBUG_LOG_FOCUS(\"SetKeyboardFocusHere() ignored while DragDropActive!\\n\");\n        return;\n    }\n\n    SetNavWindow(window);\n\n    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate | ImGuiNavMoveFlags_FocusApi | ImGuiNavMoveFlags_NoSetNavHighlight;\n    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;\n    NavMoveRequestSubmit(ImGuiDir_None, offset < 0 ? ImGuiDir_Up : ImGuiDir_Down, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.\n    if (offset == -1)\n    {\n        NavMoveRequestResolveWithLastItem(&g.NavMoveResultLocal);\n    }\n    else\n    {\n        g.NavTabbingDir = 1;\n        g.NavTabbingCounter = offset + 1;\n    }\n}\n\nvoid ImGui::SetItemDefaultFocus()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (!window->Appearing)\n        return;\n    if (g.NavWindow != window->RootWindowForNav || (!g.NavInitRequest && g.NavInitResult.ID == 0) || g.NavLayer != window->DC.NavLayerCurrent)\n        return;\n\n    g.NavInitRequest = false;\n    NavApplyItemToResult(&g.NavInitResult);\n    NavUpdateAnyRequestFlag();\n\n    // Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll)\n    if (!window->ClipRect.Contains(g.LastItemData.Rect))\n        ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None);\n}\n\nvoid ImGui::SetStateStorage(ImGuiStorage* tree)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    window->DC.StateStorage = tree ? tree : &window->StateStorage;\n}\n\nImGuiStorage* ImGui::GetStateStorage()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->DC.StateStorage;\n}\n\nvoid ImGui::PushID(const char* str_id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiID id = window->GetID(str_id);\n    window->IDStack.push_back(id);\n}\n\nvoid ImGui::PushID(const char* str_id_begin, const char* str_id_end)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiID id = window->GetID(str_id_begin, str_id_end);\n    window->IDStack.push_back(id);\n}\n\nvoid ImGui::PushID(const void* ptr_id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiID id = window->GetID(ptr_id);\n    window->IDStack.push_back(id);\n}\n\nvoid ImGui::PushID(int int_id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiID id = window->GetID(int_id);\n    window->IDStack.push_back(id);\n}\n\n// Push a given id value ignoring the ID stack as a seed.\nvoid ImGui::PushOverrideID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (g.DebugHookIdInfo == id)\n        DebugHookIdInfo(id, ImGuiDataType_ID, NULL, NULL);\n    window->IDStack.push_back(id);\n}\n\n// Helper to avoid a common series of PushOverrideID -> GetID() -> PopID() call\n// (note that when using this pattern, ID Stack Tool will tend to not display the intermediate stack level.\n//  for that to work we would need to do PushOverrideID() -> ItemAdd() -> PopID() which would alter widget code a little more)\nImGuiID ImGui::GetIDWithSeed(const char* str, const char* str_end, ImGuiID seed)\n{\n    ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);\n    ImGuiContext& g = *GImGui;\n    if (g.DebugHookIdInfo == id)\n        DebugHookIdInfo(id, ImGuiDataType_String, str, str_end);\n    return id;\n}\n\nImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed)\n{\n    ImGuiID id = ImHashData(&n, sizeof(n), seed);\n    ImGuiContext& g = *GImGui;\n    if (g.DebugHookIdInfo == id)\n        DebugHookIdInfo(id, ImGuiDataType_S32, (void*)(intptr_t)n, NULL);\n    return id;\n}\n\nvoid ImGui::PopID()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window?\n    window->IDStack.pop_back();\n}\n\nImGuiID ImGui::GetID(const char* str_id)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->GetID(str_id);\n}\n\nImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->GetID(str_id_begin, str_id_end);\n}\n\nImGuiID ImGui::GetID(const void* ptr_id)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->GetID(ptr_id);\n}\n\nbool ImGui::IsRectVisible(const ImVec2& size)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size));\n}\n\nbool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max)\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ClipRect.Overlaps(ImRect(rect_min, rect_max));\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] INPUTS\n//-----------------------------------------------------------------------------\n// - GetKeyData() [Internal]\n// - GetKeyIndex() [Internal]\n// - GetKeyName()\n// - GetKeyChordName() [Internal]\n// - CalcTypematicRepeatAmount() [Internal]\n// - GetTypematicRepeatRate() [Internal]\n// - GetKeyPressedAmount() [Internal]\n// - GetKeyMagnitude2d() [Internal]\n//-----------------------------------------------------------------------------\n// - UpdateKeyRoutingTable() [Internal]\n// - GetRoutingIdFromOwnerId() [Internal]\n// - GetShortcutRoutingData() [Internal]\n// - CalcRoutingScore() [Internal]\n// - SetShortcutRouting() [Internal]\n// - TestShortcutRouting() [Internal]\n//-----------------------------------------------------------------------------\n// - IsKeyDown()\n// - IsKeyPressed()\n// - IsKeyReleased()\n//-----------------------------------------------------------------------------\n// - IsMouseDown()\n// - IsMouseClicked()\n// - IsMouseReleased()\n// - IsMouseDoubleClicked()\n// - GetMouseClickedCount()\n// - IsMouseHoveringRect() [Internal]\n// - IsMouseDragPastThreshold() [Internal]\n// - IsMouseDragging()\n// - GetMousePos()\n// - SetMousePos() [Internal]\n// - GetMousePosOnOpeningCurrentPopup()\n// - IsMousePosValid()\n// - IsAnyMouseDown()\n// - GetMouseDragDelta()\n// - ResetMouseDragDelta()\n// - GetMouseCursor()\n// - SetMouseCursor()\n//-----------------------------------------------------------------------------\n// - UpdateAliasKey()\n// - GetMergedModsFromKeys()\n// - UpdateKeyboardInputs()\n// - UpdateMouseInputs()\n//-----------------------------------------------------------------------------\n// - LockWheelingWindow [Internal]\n// - FindBestWheelingWindow [Internal]\n// - UpdateMouseWheel() [Internal]\n//-----------------------------------------------------------------------------\n// - SetNextFrameWantCaptureKeyboard()\n// - SetNextFrameWantCaptureMouse()\n//-----------------------------------------------------------------------------\n// - GetInputSourceName() [Internal]\n// - DebugPrintInputEvent() [Internal]\n// - UpdateInputEvents() [Internal]\n//-----------------------------------------------------------------------------\n// - GetKeyOwner() [Internal]\n// - TestKeyOwner() [Internal]\n// - SetKeyOwner() [Internal]\n// - SetItemKeyOwner() [Internal]\n// - Shortcut() [Internal]\n//-----------------------------------------------------------------------------\n\nImGuiKeyData* ImGui::GetKeyData(ImGuiContext* ctx, ImGuiKey key)\n{\n    ImGuiContext& g = *ctx;\n\n    // Special storage location for mods\n    if (key & ImGuiMod_Mask_)\n        key = ConvertSingleModFlagToKey(ctx, key);\n\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END);\n    if (IsLegacyKey(key) && g.IO.KeyMap[key] != -1)\n        key = (ImGuiKey)g.IO.KeyMap[key];  // Remap native->imgui or imgui->native\n#else\n    IM_ASSERT(IsNamedKey(key) && \"Support for user key indices was dropped in favor of ImGuiKey. Please update backend & user code.\");\n#endif\n    return &g.IO.KeysData[key - ImGuiKey_KeysData_OFFSET];\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\nImGuiKey ImGui::GetKeyIndex(ImGuiKey key)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(IsNamedKey(key));\n    const ImGuiKeyData* key_data = GetKeyData(key);\n    return (ImGuiKey)(key_data - g.IO.KeysData);\n}\n#endif\n\n// Those names a provided for debugging purpose and are not meant to be saved persistently not compared.\nstatic const char* const GKeyNames[] =\n{\n    \"Tab\", \"LeftArrow\", \"RightArrow\", \"UpArrow\", \"DownArrow\", \"PageUp\", \"PageDown\",\n    \"Home\", \"End\", \"Insert\", \"Delete\", \"Backspace\", \"Space\", \"Enter\", \"Escape\",\n    \"LeftCtrl\", \"LeftShift\", \"LeftAlt\", \"LeftSuper\", \"RightCtrl\", \"RightShift\", \"RightAlt\", \"RightSuper\", \"Menu\",\n    \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\",\n    \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\", \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\",\n    \"F1\", \"F2\", \"F3\", \"F4\", \"F5\", \"F6\", \"F7\", \"F8\", \"F9\", \"F10\", \"F11\", \"F12\",\n    \"F13\", \"F14\", \"F15\", \"F16\", \"F17\", \"F18\", \"F19\", \"F20\", \"F21\", \"F22\", \"F23\", \"F24\",\n    \"Apostrophe\", \"Comma\", \"Minus\", \"Period\", \"Slash\", \"Semicolon\", \"Equal\", \"LeftBracket\",\n    \"Backslash\", \"RightBracket\", \"GraveAccent\", \"CapsLock\", \"ScrollLock\", \"NumLock\", \"PrintScreen\",\n    \"Pause\", \"Keypad0\", \"Keypad1\", \"Keypad2\", \"Keypad3\", \"Keypad4\", \"Keypad5\", \"Keypad6\",\n    \"Keypad7\", \"Keypad8\", \"Keypad9\", \"KeypadDecimal\", \"KeypadDivide\", \"KeypadMultiply\",\n    \"KeypadSubtract\", \"KeypadAdd\", \"KeypadEnter\", \"KeypadEqual\",\n    \"AppBack\", \"AppForward\",\n    \"GamepadStart\", \"GamepadBack\",\n    \"GamepadFaceLeft\", \"GamepadFaceRight\", \"GamepadFaceUp\", \"GamepadFaceDown\",\n    \"GamepadDpadLeft\", \"GamepadDpadRight\", \"GamepadDpadUp\", \"GamepadDpadDown\",\n    \"GamepadL1\", \"GamepadR1\", \"GamepadL2\", \"GamepadR2\", \"GamepadL3\", \"GamepadR3\",\n    \"GamepadLStickLeft\", \"GamepadLStickRight\", \"GamepadLStickUp\", \"GamepadLStickDown\",\n    \"GamepadRStickLeft\", \"GamepadRStickRight\", \"GamepadRStickUp\", \"GamepadRStickDown\",\n    \"MouseLeft\", \"MouseRight\", \"MouseMiddle\", \"MouseX1\", \"MouseX2\", \"MouseWheelX\", \"MouseWheelY\",\n    \"ModCtrl\", \"ModShift\", \"ModAlt\", \"ModSuper\", // ReservedForModXXX are showing the ModXXX names.\n};\nIM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames));\n\nconst char* ImGui::GetKeyName(ImGuiKey key)\n{\n    ImGuiContext& g = *GImGui;\n#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO\n    IM_ASSERT((IsNamedKeyOrModKey(key) || key == ImGuiKey_None) && \"Support for user key indices was dropped in favor of ImGuiKey. Please update backend and user code.\");\n#else\n    if (IsLegacyKey(key))\n    {\n        if (g.IO.KeyMap[key] == -1)\n            return \"N/A\";\n        IM_ASSERT(IsNamedKey((ImGuiKey)g.IO.KeyMap[key]));\n        key = (ImGuiKey)g.IO.KeyMap[key];\n    }\n#endif\n    if (key == ImGuiKey_None)\n        return \"None\";\n    if (key & ImGuiMod_Mask_)\n        key = ConvertSingleModFlagToKey(&g, key);\n    if (!IsNamedKey(key))\n        return \"Unknown\";\n\n    return GKeyNames[key - ImGuiKey_NamedKey_BEGIN];\n}\n\n// ImGuiMod_Shortcut is translated to either Ctrl or Super.\nvoid ImGui::GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size)\n{\n    ImGuiContext& g = *GImGui;\n    if (key_chord & ImGuiMod_Shortcut)\n        key_chord = ConvertShortcutMod(key_chord);\n    ImFormatString(out_buf, (size_t)out_buf_size, \"%s%s%s%s%s\",\n        (key_chord & ImGuiMod_Ctrl) ? \"Ctrl+\" : \"\",\n        (key_chord & ImGuiMod_Shift) ? \"Shift+\" : \"\",\n        (key_chord & ImGuiMod_Alt) ? \"Alt+\" : \"\",\n        (key_chord & ImGuiMod_Super) ? (g.IO.ConfigMacOSXBehaviors ? \"Cmd+\" : \"Super+\") : \"\",\n        GetKeyName((ImGuiKey)(key_chord & ~ImGuiMod_Mask_)));\n}\n\n// t0 = previous time (e.g.: g.Time - g.IO.DeltaTime)\n// t1 = current time (e.g.: g.Time)\n// An event is triggered at:\n//  t = 0.0f     t = repeat_delay,    t = repeat_delay + repeat_rate*N\nint ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate)\n{\n    if (t1 == 0.0f)\n        return 1;\n    if (t0 >= t1)\n        return 0;\n    if (repeat_rate <= 0.0f)\n        return (t0 < repeat_delay) && (t1 >= repeat_delay);\n    const int count_t0 = (t0 < repeat_delay) ? -1 : (int)((t0 - repeat_delay) / repeat_rate);\n    const int count_t1 = (t1 < repeat_delay) ? -1 : (int)((t1 - repeat_delay) / repeat_rate);\n    const int count = count_t1 - count_t0;\n    return count;\n}\n\nvoid ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate)\n{\n    ImGuiContext& g = *GImGui;\n    switch (flags & ImGuiInputFlags_RepeatRateMask_)\n    {\n    case ImGuiInputFlags_RepeatRateNavMove:             *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return;\n    case ImGuiInputFlags_RepeatRateNavTweak:            *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return;\n    case ImGuiInputFlags_RepeatRateDefault: default:    *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return;\n    }\n}\n\n// Return value representing the number of presses in the last time period, for the given repeat rate\n// (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate)\nint ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiKeyData* key_data = GetKeyData(key);\n    if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)\n        return 0;\n    const float t = key_data->DownDuration;\n    return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate);\n}\n\n// Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values).\nImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down)\n{\n    return ImVec2(\n        GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue,\n        GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue);\n}\n\n// Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data.\n//   Entries   D,A,B,B,A,C,B     --> A,A,B,B,B,C,D\n//   Index     A:1 B:2 C:5 D:0   --> A:0 B:2 C:5 D:6\n// See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation.\nstatic void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt)\n{\n    ImGuiContext& g = *GImGui;\n    rt->EntriesNext.resize(0);\n    for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))\n    {\n        const int new_routing_start_idx = rt->EntriesNext.Size;\n        ImGuiKeyRoutingData* routing_entry;\n        for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex)\n        {\n            routing_entry = &rt->Entries[old_routing_idx];\n            routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry\n            routing_entry->RoutingNext = ImGuiKeyOwner_None;\n            routing_entry->RoutingNextScore = 255;\n            if (routing_entry->RoutingCurr == ImGuiKeyOwner_None)\n                continue;\n            rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer\n\n            // Apply routing to owner if there's no owner already (RoutingCurr == None at this point)\n            if (routing_entry->Mods == g.IO.KeyMods)\n            {\n                ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);\n                if (owner_data->OwnerCurr == ImGuiKeyOwner_None)\n                    owner_data->OwnerCurr = routing_entry->RoutingCurr;\n            }\n        }\n\n        // Rewrite linked-list\n        rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1);\n        for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++)\n            rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1);\n    }\n    rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes\n}\n\n// owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope().\nstatic inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    return (owner_id != ImGuiKeyOwner_None && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId;\n}\n\nImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord)\n{\n    // Majority of shortcuts will be Key + any number of Mods\n    // We accept _Single_ mod with ImGuiKey_None.\n    //  - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl);                    // Legal\n    //  - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift);   // Legal\n    //  - Shortcut(ImGuiMod_Ctrl);                                 // Legal\n    //  - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift);                // Not legal\n    ImGuiContext& g = *GImGui;\n    ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;\n    ImGuiKeyRoutingData* routing_data;\n    if (key_chord & ImGuiMod_Shortcut)\n        key_chord = ConvertShortcutMod(key_chord);\n    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);\n    ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);\n    if (key == ImGuiKey_None)\n        key = ConvertSingleModFlagToKey(&g, mods);\n    IM_ASSERT(IsNamedKey(key));\n\n    // Get (in the majority of case, the linked list will have one element so this should be 2 reads.\n    // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame).\n    for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex)\n    {\n        routing_data = &rt->Entries[idx];\n        if (routing_data->Mods == mods)\n            return routing_data;\n    }\n\n    // Add to linked-list\n    ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size;\n    rt->Entries.push_back(ImGuiKeyRoutingData());\n    routing_data = &rt->Entries[routing_data_idx];\n    routing_data->Mods = (ImU16)mods;\n    routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list\n    rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx;\n    return routing_data;\n}\n\n// Current score encoding (lower is highest priority):\n//  -   0: ImGuiInputFlags_RouteGlobalHigh\n//  -   1: ImGuiInputFlags_RouteFocused (if item active)\n//  -   2: ImGuiInputFlags_RouteGlobal\n//  -  3+: ImGuiInputFlags_RouteFocused (if window in focus-stack)\n//  - 254: ImGuiInputFlags_RouteGlobalLow\n//  - 255: never route\n// 'flags' should include an explicit routing policy\nstatic int CalcRoutingScore(ImGuiWindow* location, ImGuiID owner_id, ImGuiInputFlags flags)\n{\n    if (flags & ImGuiInputFlags_RouteFocused)\n    {\n        ImGuiContext& g = *GImGui;\n        ImGuiWindow* focused = g.NavWindow;\n\n        // ActiveID gets top priority\n        // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it)\n        if (owner_id != 0 && g.ActiveId == owner_id)\n            return 1;\n\n        // Score based on distance to focused window (lower is better)\n        // Assuming both windows are submitting a routing request,\n        // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match)\n        // - When Window/ChildB is focused -> Window scores 4,        Window/ChildB scores 3 (best)\n        // Assuming only WindowA is submitting a routing request,\n        // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score.\n        if (focused != NULL && focused->RootWindow == location->RootWindow)\n            for (int next_score = 3; focused != NULL; next_score++)\n            {\n                if (focused == location)\n                {\n                    IM_ASSERT(next_score < 255);\n                    return next_score;\n                }\n                focused = (focused->RootWindow != focused) ? focused->ParentWindow : NULL; // FIXME: This could be later abstracted as a focus path\n            }\n        return 255;\n    }\n\n    // ImGuiInputFlags_RouteGlobalHigh is default, so calls without flags are not conditional\n    if (flags & ImGuiInputFlags_RouteGlobal)\n        return 2;\n    if (flags & ImGuiInputFlags_RouteGlobalLow)\n        return 254;\n    return 0;\n}\n\n// Request a desired route for an input chord (key + mods).\n// Return true if the route is available this frame.\n// - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state.\n//   (Conceptually this does a \"Submit for next frame\" + \"Test for current frame\".\n//   As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.)\n// - Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default)\n// - Using 'owner_id == ImGuiKeyOwner_None': allows disabling/locking a shortcut.\nbool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if ((flags & ImGuiInputFlags_RouteMask_) == 0)\n        flags |= ImGuiInputFlags_RouteGlobalHigh; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut()\n    else\n        IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteMask_)); // Check that only 1 routing flag is used\n\n    if (flags & ImGuiInputFlags_RouteUnlessBgFocused)\n        if (g.NavWindow == NULL)\n            return false;\n    if (flags & ImGuiInputFlags_RouteAlways)\n        return true;\n\n    const int score = CalcRoutingScore(g.CurrentWindow, owner_id, flags);\n    if (score == 255)\n        return false;\n\n    // Submit routing for NEXT frame (assuming score is sufficient)\n    // FIXME: Could expose a way to use a \"serve last\" policy for same score resolution (using <= instead of <).\n    ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord);\n    const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);\n    //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore);\n    if (score < routing_data->RoutingNextScore)\n    {\n        routing_data->RoutingNext = routing_id;\n        routing_data->RoutingNextScore = (ImU8)score;\n    }\n\n    // Return routing state for CURRENT frame\n    return routing_data->RoutingCurr == routing_id;\n}\n\n// Currently unused by core (but used by tests)\n// Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading.\nbool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id)\n{\n    const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id);\n    ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry.\n    return routing_data->RoutingCurr == routing_id;\n}\n\n// Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes.\n// Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87)\nbool ImGui::IsKeyDown(ImGuiKey key)\n{\n    return IsKeyDown(key, ImGuiKeyOwner_Any);\n}\n\nbool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id)\n{\n    const ImGuiKeyData* key_data = GetKeyData(key);\n    if (!key_data->Down)\n        return false;\n    if (!TestKeyOwner(key, owner_id))\n        return false;\n    return true;\n}\n\nbool ImGui::IsKeyPressed(ImGuiKey key, bool repeat)\n{\n    return IsKeyPressed(key, ImGuiKeyOwner_Any, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None);\n}\n\n// Important: unless legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat.\nbool ImGui::IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)\n{\n    const ImGuiKeyData* key_data = GetKeyData(key);\n    if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)\n        return false;\n    const float t = key_data->DownDuration;\n    if (t < 0.0f)\n        return false;\n    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!\n\n    bool pressed = (t == 0.0f);\n    if (!pressed && ((flags & ImGuiInputFlags_Repeat) != 0))\n    {\n        float repeat_delay, repeat_rate;\n        GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate);\n        pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0;\n    }\n    if (!pressed)\n        return false;\n    if (!TestKeyOwner(key, owner_id))\n        return false;\n    return true;\n}\n\nbool ImGui::IsKeyReleased(ImGuiKey key)\n{\n    return IsKeyReleased(key, ImGuiKeyOwner_Any);\n}\n\nbool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id)\n{\n    const ImGuiKeyData* key_data = GetKeyData(key);\n    if (key_data->DownDurationPrev < 0.0f || key_data->Down)\n        return false;\n    if (!TestKeyOwner(key, owner_id))\n        return false;\n    return true;\n}\n\nbool ImGui::IsMouseDown(ImGuiMouseButton button)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array.\n}\n\nbool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array.\n}\n\nbool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat)\n{\n    return IsMouseClicked(button, ImGuiKeyOwner_Any, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None);\n}\n\nbool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership)\n        return false;\n    const float t = g.IO.MouseDownDuration[button];\n    if (t < 0.0f)\n        return false;\n    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function!\n\n    const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0;\n    const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0);\n    if (!pressed)\n        return false;\n\n    if (!TestKeyOwner(MouseButtonToKey(button), owner_id))\n        return false;\n\n    return true;\n}\n\nbool ImGui::IsMouseReleased(ImGuiMouseButton button)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any)\n}\n\nbool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id)\n}\n\nbool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any);\n}\n\nbool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), owner_id);\n}\n\nint ImGui::GetMouseClickedCount(ImGuiMouseButton button)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    return g.IO.MouseClickedCount[button];\n}\n\n// Test if mouse cursor is hovering given rectangle\n// NB- Rectangle is clipped by our current clip setting\n// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding)\nbool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Clip\n    ImRect rect_clipped(r_min, r_max);\n    if (clip)\n        rect_clipped.ClipWith(g.CurrentWindow->ClipRect);\n\n    // Hit testing, expanded for touch input\n    if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding))\n        return false;\n    return true;\n}\n\n// Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame.\n// [Internal] This doesn't test if the button is pressed\nbool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    if (lock_threshold < 0.0f)\n        lock_threshold = g.IO.MouseDragThreshold;\n    return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold;\n}\n\nbool ImGui::IsMouseDragging(ImGuiMouseButton button, float lock_threshold)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    if (!g.IO.MouseDown[button])\n        return false;\n    return IsMouseDragPastThreshold(button, lock_threshold);\n}\n\nImVec2 ImGui::GetMousePos()\n{\n    ImGuiContext& g = *GImGui;\n    return g.IO.MousePos;\n}\n\n// This is called TeleportMousePos() and not SetMousePos() to emphasis that setting MousePosPrev will effectively clear mouse delta as well.\n// It is expected you only call this if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) is set and supported by backend.\nvoid ImGui::TeleportMousePos(const ImVec2& pos)\n{\n    ImGuiContext& g = *GImGui;\n    g.IO.MousePos = g.IO.MousePosPrev = pos;\n    g.IO.MouseDelta = ImVec2(0.0f, 0.0f);\n    g.IO.WantSetMousePos = true;\n    //IMGUI_DEBUG_LOG_IO(\"TeleportMousePos: (%.1f,%.1f)\\n\", io.MousePos.x, io.MousePos.y);\n}\n\n// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed!\nImVec2 ImGui::GetMousePosOnOpeningCurrentPopup()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.BeginPopupStack.Size > 0)\n        return g.OpenPopupStack[g.BeginPopupStack.Size - 1].OpenMousePos;\n    return g.IO.MousePos;\n}\n\n// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position.\nbool ImGui::IsMousePosValid(const ImVec2* mouse_pos)\n{\n    // The assert is only to silence a false-positive in XCode Static Analysis.\n    // Because GImGui is not dereferenced in every code path, the static analyzer assume that it may be NULL (which it doesn't for other functions).\n    IM_ASSERT(GImGui != NULL);\n    const float MOUSE_INVALID = -256000.0f;\n    ImVec2 p = mouse_pos ? *mouse_pos : GImGui->IO.MousePos;\n    return p.x >= MOUSE_INVALID && p.y >= MOUSE_INVALID;\n}\n\n// [WILL OBSOLETE] This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.\nbool ImGui::IsAnyMouseDown()\n{\n    ImGuiContext& g = *GImGui;\n    for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++)\n        if (g.IO.MouseDown[n])\n            return true;\n    return false;\n}\n\n// Return the delta from the initial clicking position while the mouse button is clicked or was just released.\n// This is locked and return 0.0f until the mouse moves past a distance threshold at least once.\n// NB: This is only valid if IsMousePosValid(). backends in theory should always keep mouse position valid when dragging even outside the client window.\nImVec2 ImGui::GetMouseDragDelta(ImGuiMouseButton button, float lock_threshold)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    if (lock_threshold < 0.0f)\n        lock_threshold = g.IO.MouseDragThreshold;\n    if (g.IO.MouseDown[button] || g.IO.MouseReleased[button])\n        if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold)\n            if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MouseClickedPos[button]))\n                return g.IO.MousePos - g.IO.MouseClickedPos[button];\n    return ImVec2(0.0f, 0.0f);\n}\n\nvoid ImGui::ResetMouseDragDelta(ImGuiMouseButton button)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown));\n    // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr\n    g.IO.MouseClickedPos[button] = g.IO.MousePos;\n}\n\n// Get desired mouse cursor shape.\n// Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(),\n// updated during the frame, and locked in EndFrame()/Render().\n// If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you\nImGuiMouseCursor ImGui::GetMouseCursor()\n{\n    ImGuiContext& g = *GImGui;\n    return g.MouseCursor;\n}\n\nvoid ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type)\n{\n    ImGuiContext& g = *GImGui;\n    g.MouseCursor = cursor_type;\n}\n\nstatic void UpdateAliasKey(ImGuiKey key, bool v, float analog_value)\n{\n    IM_ASSERT(ImGui::IsAliasKey(key));\n    ImGuiKeyData* key_data = ImGui::GetKeyData(key);\n    key_data->Down = v;\n    key_data->AnalogValue = analog_value;\n}\n\n// [Internal] Do not use directly\nstatic ImGuiKeyChord GetMergedModsFromKeys()\n{\n    ImGuiKeyChord mods = 0;\n    if (ImGui::IsKeyDown(ImGuiMod_Ctrl))     { mods |= ImGuiMod_Ctrl; }\n    if (ImGui::IsKeyDown(ImGuiMod_Shift))    { mods |= ImGuiMod_Shift; }\n    if (ImGui::IsKeyDown(ImGuiMod_Alt))      { mods |= ImGuiMod_Alt; }\n    if (ImGui::IsKeyDown(ImGuiMod_Super))    { mods |= ImGuiMod_Super; }\n    return mods;\n}\n\nstatic void ImGui::UpdateKeyboardInputs()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    // Import legacy keys or verify they are not used\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    if (io.BackendUsingLegacyKeyArrays == 0)\n    {\n        // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally.\n        for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++)\n            IM_ASSERT((io.KeysDown[n] == false || IsKeyDown((ImGuiKey)n)) && \"Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!\");\n    }\n    else\n    {\n        if (g.FrameCount == 0)\n            for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)\n                IM_ASSERT(g.IO.KeyMap[n] == -1 && \"Backend is not allowed to write to io.KeyMap[0..511]!\");\n\n        // Build reverse KeyMap (Named -> Legacy)\n        for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++)\n            if (io.KeyMap[n] != -1)\n            {\n                IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n]));\n                io.KeyMap[io.KeyMap[n]] = n;\n            }\n\n        // Import legacy keys into new ones\n        for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++)\n            if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1)\n            {\n                const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n);\n                IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key));\n                io.KeysData[key].Down = io.KeysDown[n];\n                if (key != n)\n                    io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends\n                io.BackendUsingLegacyKeyArrays = 1;\n            }\n        if (io.BackendUsingLegacyKeyArrays == 1)\n        {\n            GetKeyData(ImGuiMod_Ctrl)->Down = io.KeyCtrl;\n            GetKeyData(ImGuiMod_Shift)->Down = io.KeyShift;\n            GetKeyData(ImGuiMod_Alt)->Down = io.KeyAlt;\n            GetKeyData(ImGuiMod_Super)->Down = io.KeySuper;\n        }\n    }\n\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;\n    if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active)\n    {\n        #define MAP_LEGACY_NAV_INPUT_TO_KEY1(_KEY, _NAV1)           do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f); io.KeysData[_KEY].AnalogValue = io.NavInputs[_NAV1]; } while (0)\n        #define MAP_LEGACY_NAV_INPUT_TO_KEY2(_KEY, _NAV1, _NAV2)    do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f) || (io.NavInputs[_NAV2] > 0.0f); io.KeysData[_KEY].AnalogValue = ImMax(io.NavInputs[_NAV1], io.NavInputs[_NAV2]); } while (0)\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate);\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel);\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu);\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input);\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft);\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight);\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp);\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown);\n        MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, ImGuiNavInput_TweakSlow);\n        MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakFast);\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft);\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight);\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp);\n        MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown);\n        #undef NAV_MAP_KEY\n    }\n#endif\n#endif\n\n    // Update aliases\n    for (int n = 0; n < ImGuiMouseButton_COUNT; n++)\n        UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f);\n    UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH);\n    UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel);\n\n    // Synchronize io.KeyMods and io.KeyXXX values.\n    // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) ->                                      -> (here) deriving io.KeyMods + io.KeyXXX from key array.\n    // - Legacy backends:      set io.KeyXXX bools               -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array.\n    // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing.\n    io.KeyMods = GetMergedModsFromKeys();\n    io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0;\n    io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0;\n    io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0;\n    io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0;\n\n    // Clear gamepad data if disabled\n    if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0)\n        for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++)\n        {\n            io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false;\n            io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f;\n        }\n\n    // Update keys\n    for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++)\n    {\n        ImGuiKeyData* key_data = &io.KeysData[i];\n        key_data->DownDurationPrev = key_data->DownDuration;\n        key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f;\n    }\n\n    // Update keys/input owner (named keys only): one entry per key\n    for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))\n    {\n        ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_KeysData_OFFSET];\n        ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN];\n        owner_data->OwnerCurr = owner_data->OwnerNext;\n        if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp.\n            owner_data->OwnerNext = ImGuiKeyOwner_None;\n        owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down;  // Clear LockUntilRelease when key is not Down anymore\n    }\n\n    UpdateKeyRoutingTable(&g.KeysRoutingTable);\n}\n\nstatic void ImGui::UpdateMouseInputs()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    // Mouse Wheel swapping flag\n    // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead\n    // - We avoid doing it on OSX as it the OS input layer handles this already.\n    // - FIXME: However this means when running on OSX over Emscripten, Shift+WheelY will incur two swapping (1 in OS, 1 here), canceling the feature.\n    // - FIXME: When we can distinguish e.g. touchpad scroll events from mouse ones, we'll set this accordingly based on input source.\n    io.MouseWheelRequestAxisSwap = io.KeyShift && !io.ConfigMacOSXBehaviors;\n\n    // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well)\n    if (IsMousePosValid(&io.MousePos))\n        io.MousePos = g.MouseLastValidPos = ImFloor(io.MousePos);\n\n    // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta\n    if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev))\n        io.MouseDelta = io.MousePos - io.MousePosPrev;\n    else\n        io.MouseDelta = ImVec2(0.0f, 0.0f);\n\n    // Update stationary timer.\n    // FIXME: May need to rework again to have some tolerance for occasional small movement, while being functional on high-framerates.\n    const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.\n    const bool mouse_stationary = (ImLengthSqr(io.MouseDelta) <= mouse_stationary_threshold * mouse_stationary_threshold);\n    g.MouseStationaryTimer = mouse_stationary ? (g.MouseStationaryTimer + io.DeltaTime) : 0.0f;\n    //IMGUI_DEBUG_LOG(\"%.4f\\n\", g.MouseStationaryTimer);\n\n    // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.\n    if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)\n        g.NavDisableMouseHover = false;\n\n    for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++)\n    {\n        io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f;\n        io.MouseClickedCount[i] = 0; // Will be filled below\n        io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f;\n        io.MouseDownDurationPrev[i] = io.MouseDownDuration[i];\n        io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f;\n        if (io.MouseClicked[i])\n        {\n            bool is_repeated_click = false;\n            if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime)\n            {\n                ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f);\n                if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist)\n                    is_repeated_click = true;\n            }\n            if (is_repeated_click)\n                io.MouseClickedLastCount[i]++;\n            else\n                io.MouseClickedLastCount[i] = 1;\n            io.MouseClickedTime[i] = g.Time;\n            io.MouseClickedPos[i] = io.MousePos;\n            io.MouseClickedCount[i] = io.MouseClickedLastCount[i];\n            io.MouseDragMaxDistanceSqr[i] = 0.0f;\n        }\n        else if (io.MouseDown[i])\n        {\n            // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold\n            float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f;\n            io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos);\n        }\n\n        // We provide io.MouseDoubleClicked[] as a legacy service\n        io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2);\n\n        // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation\n        if (io.MouseClicked[i])\n            g.NavDisableMouseHover = false;\n    }\n}\n\nstatic void LockWheelingWindow(ImGuiWindow* window, float wheel_amount)\n{\n    ImGuiContext& g = *GImGui;\n    if (window)\n        g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER);\n    else\n        g.WheelingWindowReleaseTimer = 0.0f;\n    if (g.WheelingWindow == window)\n        return;\n    IMGUI_DEBUG_LOG_IO(\"[io] LockWheelingWindow() \\\"%s\\\"\\n\", window ? window->Name : \"NULL\");\n    g.WheelingWindow = window;\n    g.WheelingWindowRefMousePos = g.IO.MousePos;\n    if (window == NULL)\n    {\n        g.WheelingWindowStartFrame = -1;\n        g.WheelingAxisAvg = ImVec2(0.0f, 0.0f);\n    }\n}\n\nstatic ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel)\n{\n    // For each axis, find window in the hierarchy that may want to use scrolling\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* windows[2] = { NULL, NULL };\n    for (int axis = 0; axis < 2; axis++)\n        if (wheel[axis] != 0.0f)\n            for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow)\n            {\n                // Bubble up into parent window if:\n                // - a child window doesn't allow any scrolling.\n                // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag.\n                //// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP)\n                const bool has_scrolling = (window->ScrollMax[axis] != 0.0f);\n                const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs);\n                //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]);\n                if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits)\n                    break; // select this window\n            }\n    if (windows[0] == NULL && windows[1] == NULL)\n        return NULL;\n\n    // If there's only one window or only one axis then there's no ambiguity\n    if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL)\n        return windows[1] ? windows[1] : windows[0];\n\n    // If candidate are different windows we need to decide which one to prioritize\n    // - First frame: only find a winner if one axis is zero.\n    // - Subsequent frames: only find a winner when one is more than the other.\n    if (g.WheelingWindowStartFrame == -1)\n        g.WheelingWindowStartFrame = g.FrameCount;\n    if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y))\n    {\n        g.WheelingWindowWheelRemainder = wheel;\n        return NULL;\n    }\n    return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1];\n}\n\n// Called by NewFrame()\nvoid ImGui::UpdateMouseWheel()\n{\n    // Reset the locked window if we move the mouse or after the timer elapses.\n    // FIXME: Ideally we could refactor to have one timer for \"changing window w/ same axis\" and a shorter timer for \"changing window or axis w/ other axis\" (#3795)\n    ImGuiContext& g = *GImGui;\n    if (g.WheelingWindow != NULL)\n    {\n        g.WheelingWindowReleaseTimer -= g.IO.DeltaTime;\n        if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold)\n            g.WheelingWindowReleaseTimer = 0.0f;\n        if (g.WheelingWindowReleaseTimer <= 0.0f)\n            LockWheelingWindow(NULL, 0.0f);\n    }\n\n    ImVec2 wheel;\n    wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_None) ? g.IO.MouseWheelH : 0.0f;\n    wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_None) ? g.IO.MouseWheel : 0.0f;\n\n    //IMGUI_DEBUG_LOG(\"MouseWheel X:%.3f Y:%.3f\\n\", wheel_x, wheel_y);\n    ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow;\n    if (!mouse_window || mouse_window->Collapsed)\n        return;\n\n    // Zoom / Scale window\n    // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned.\n    if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling)\n    {\n        LockWheelingWindow(mouse_window, wheel.y);\n        ImGuiWindow* window = mouse_window;\n        const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f);\n        const float scale = new_font_scale / window->FontWindowScale;\n        window->FontWindowScale = new_font_scale;\n        if (window == window->RootWindow)\n        {\n            const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size;\n            SetWindowPos(window, window->Pos + offset, 0);\n            window->Size = ImTrunc(window->Size * scale);\n            window->SizeFull = ImTrunc(window->SizeFull * scale);\n        }\n        return;\n    }\n    if (g.IO.KeyCtrl)\n        return;\n\n    // Mouse wheel scrolling\n    // Read about io.MouseWheelRequestAxisSwap and its issue on Mac+Emscripten in UpdateMouseInputs()\n    if (g.IO.MouseWheelRequestAxisSwap)\n        wheel = ImVec2(wheel.y, 0.0f);\n\n    // Maintain a rough average of moving magnitude on both axises\n    // FIXME: should by based on wall clock time rather than frame-counter\n    g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30);\n    g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30);\n\n    // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now.\n    wheel += g.WheelingWindowWheelRemainder;\n    g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f);\n    if (wheel.x == 0.0f && wheel.y == 0.0f)\n        return;\n\n    // Mouse wheel scrolling: find target and apply\n    // - don't renew lock if axis doesn't apply on the window.\n    // - select a main axis when both axises are being moved.\n    if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel)))\n        if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs))\n        {\n            bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f };\n            if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y])\n                do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false;\n            if (do_scroll[ImGuiAxis_X])\n            {\n                LockWheelingWindow(window, wheel.x);\n                float max_step = window->InnerRect.GetWidth() * 0.67f;\n                float scroll_step = ImTrunc(ImMin(2 * window->CalcFontSize(), max_step));\n                SetScrollX(window, window->Scroll.x - wheel.x * scroll_step);\n                g.WheelingWindowScrolledFrame = g.FrameCount;\n            }\n            if (do_scroll[ImGuiAxis_Y])\n            {\n                LockWheelingWindow(window, wheel.y);\n                float max_step = window->InnerRect.GetHeight() * 0.67f;\n                float scroll_step = ImTrunc(ImMin(5 * window->CalcFontSize(), max_step));\n                SetScrollY(window, window->Scroll.y - wheel.y * scroll_step);\n                g.WheelingWindowScrolledFrame = g.FrameCount;\n            }\n        }\n}\n\nvoid ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard)\n{\n    ImGuiContext& g = *GImGui;\n    g.WantCaptureKeyboardNextFrame = want_capture_keyboard ? 1 : 0;\n}\n\nvoid ImGui::SetNextFrameWantCaptureMouse(bool want_capture_mouse)\n{\n    ImGuiContext& g = *GImGui;\n    g.WantCaptureMouseNextFrame = want_capture_mouse ? 1 : 0;\n}\n\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\nstatic const char* GetInputSourceName(ImGuiInputSource source)\n{\n    const char* input_source_names[] = { \"None\", \"Mouse\", \"Keyboard\", \"Gamepad\", \"Clipboard\" };\n    IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT);\n    return input_source_names[source];\n}\nstatic const char* GetMouseSourceName(ImGuiMouseSource source)\n{\n    const char* mouse_source_names[] = { \"Mouse\", \"TouchScreen\", \"Pen\" };\n    IM_ASSERT(IM_ARRAYSIZE(mouse_source_names) == ImGuiMouseSource_COUNT && source >= 0 && source < ImGuiMouseSource_COUNT);\n    return mouse_source_names[source];\n}\nstatic void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e)\n{\n    ImGuiContext& g = *GImGui;\n    if (e->Type == ImGuiInputEventType_MousePos)    { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO(\"[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\\n\", prefix); else IMGUI_DEBUG_LOG_IO(\"[io] %s: MousePos (%.1f, %.1f) (%s)\\n\", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; }\n    if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO(\"[io] %s: MouseButton %d %s (%s)\\n\", prefix, e->MouseButton.Button, e->MouseButton.Down ? \"Down\" : \"Up\", GetMouseSourceName(e->MouseButton.MouseSource)); return; }\n    if (e->Type == ImGuiInputEventType_MouseWheel)  { IMGUI_DEBUG_LOG_IO(\"[io] %s: MouseWheel (%.3f, %.3f) (%s)\\n\", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; }\n    if (e->Type == ImGuiInputEventType_Key)         { IMGUI_DEBUG_LOG_IO(\"[io] %s: Key \\\"%s\\\" %s\\n\", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? \"Down\" : \"Up\"); return; }\n    if (e->Type == ImGuiInputEventType_Text)        { IMGUI_DEBUG_LOG_IO(\"[io] %s: Text: %c (U+%08X)\\n\", prefix, e->Text.Char, e->Text.Char); return; }\n    if (e->Type == ImGuiInputEventType_Focus)       { IMGUI_DEBUG_LOG_IO(\"[io] %s: AppFocused %d\\n\", prefix, e->AppFocused.Focused); return; }\n}\n#endif\n\n// Process input queue\n// We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'.\n// - trickle_fast_inputs = false : process all events, turn into flattened input state (e.g. successive down/up/down/up will be lost)\n// - trickle_fast_inputs = true  : process as many events as possible (successive down/up/down/up will be trickled over several frames so nothing is lost) (new feature in 1.87)\nvoid ImGui::UpdateInputEvents(bool trickle_fast_inputs)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    // Only trickle chars<>key when working with InputText()\n    // FIXME: InputText() could parse event trail?\n    // FIXME: Could specialize chars<>keys trickling rules for control keys (those not typically associated to characters)\n    const bool trickle_interleaved_keys_and_text = (trickle_fast_inputs && g.WantTextInputNextFrame == 1);\n\n    bool mouse_moved = false, mouse_wheeled = false, key_changed = false, text_inputted = false;\n    int  mouse_button_changed = 0x00;\n    ImBitArray<ImGuiKey_KeysData_SIZE> key_changed_mask;\n\n    int event_n = 0;\n    for (; event_n < g.InputEventsQueue.Size; event_n++)\n    {\n        ImGuiInputEvent* e = &g.InputEventsQueue[event_n];\n        if (e->Type == ImGuiInputEventType_MousePos)\n        {\n            if (g.IO.WantSetMousePos)\n                continue;\n            // Trickling Rule: Stop processing queued events if we already handled a mouse button change\n            ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY);\n            if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted))\n                break;\n            io.MousePos = event_pos;\n            io.MouseSource = e->MousePos.MouseSource;\n            mouse_moved = true;\n        }\n        else if (e->Type == ImGuiInputEventType_MouseButton)\n        {\n            // Trickling Rule: Stop processing queued events if we got multiple action on the same button\n            const ImGuiMouseButton button = e->MouseButton.Button;\n            IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT);\n            if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled))\n                break;\n            if (trickle_fast_inputs && e->MouseButton.MouseSource == ImGuiMouseSource_TouchScreen && mouse_moved) // #2702: TouchScreen have no initial hover.\n                break;\n            io.MouseDown[button] = e->MouseButton.Down;\n            io.MouseSource = e->MouseButton.MouseSource;\n            mouse_button_changed |= (1 << button);\n        }\n        else if (e->Type == ImGuiInputEventType_MouseWheel)\n        {\n            // Trickling Rule: Stop processing queued events if we got multiple action on the event\n            if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0))\n                break;\n            io.MouseWheelH += e->MouseWheel.WheelX;\n            io.MouseWheel += e->MouseWheel.WheelY;\n            io.MouseSource = e->MouseWheel.MouseSource;\n            mouse_wheeled = true;\n        }\n        else if (e->Type == ImGuiInputEventType_Key)\n        {\n            // Trickling Rule: Stop processing queued events if we got multiple action on the same button\n            ImGuiKey key = e->Key.Key;\n            IM_ASSERT(key != ImGuiKey_None);\n            ImGuiKeyData* key_data = GetKeyData(key);\n            const int key_data_index = (int)(key_data - g.IO.KeysData);\n            if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || text_inputted || mouse_button_changed != 0))\n                break;\n            key_data->Down = e->Key.Down;\n            key_data->AnalogValue = e->Key.AnalogValue;\n            key_changed = true;\n            key_changed_mask.SetBit(key_data_index);\n\n            // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n            io.KeysDown[key_data_index] = key_data->Down;\n            if (io.KeyMap[key_data_index] != -1)\n                io.KeysDown[io.KeyMap[key_data_index]] = key_data->Down;\n#endif\n        }\n        else if (e->Type == ImGuiInputEventType_Text)\n        {\n            // Trickling Rule: Stop processing queued events if keys/mouse have been interacted with\n            if (trickle_fast_inputs && ((key_changed && trickle_interleaved_keys_and_text) || mouse_button_changed != 0 || mouse_moved || mouse_wheeled))\n                break;\n            unsigned int c = e->Text.Char;\n            io.InputQueueCharacters.push_back(c <= IM_UNICODE_CODEPOINT_MAX ? (ImWchar)c : IM_UNICODE_CODEPOINT_INVALID);\n            if (trickle_interleaved_keys_and_text)\n                text_inputted = true;\n        }\n        else if (e->Type == ImGuiInputEventType_Focus)\n        {\n            // We intentionally overwrite this and process in NewFrame(), in order to give a chance\n            // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame.\n            const bool focus_lost = !e->AppFocused.Focused;\n            io.AppFocusLost = focus_lost;\n        }\n        else\n        {\n            IM_ASSERT(0 && \"Unknown event!\");\n        }\n    }\n\n    // Record trail (for domain-specific applications wanting to access a precise trail)\n    //if (event_n != 0) IMGUI_DEBUG_LOG_IO(\"Processed: %d / Remaining: %d\\n\", event_n, g.InputEventsQueue.Size - event_n);\n    for (int n = 0; n < event_n; n++)\n        g.InputEventsTrail.push_back(g.InputEventsQueue[n]);\n\n    // [DEBUG]\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO))\n        for (int n = 0; n < g.InputEventsQueue.Size; n++)\n            DebugPrintInputEvent(n < event_n ? \"Processed\" : \"Remaining\", &g.InputEventsQueue[n]);\n#endif\n\n    // Remaining events will be processed on the next frame\n    if (event_n == g.InputEventsQueue.Size)\n        g.InputEventsQueue.resize(0);\n    else\n        g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n);\n\n    // Clear buttons state when focus is lost\n    // - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle.\n    // - we clear in EndFrame() and not now in order allow application/user code polling this flag\n    //   (e.g. custom backend may want to clear additional data, custom widgets may want to react with a \"canceling\" event).\n    if (g.IO.AppFocusLost)\n        g.IO.ClearInputKeys();\n}\n\nImGuiID ImGui::GetKeyOwner(ImGuiKey key)\n{\n    if (!IsNamedKeyOrModKey(key))\n        return ImGuiKeyOwner_None;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);\n    ImGuiID owner_id = owner_data->OwnerCurr;\n\n    if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)\n        if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)\n            return ImGuiKeyOwner_None;\n\n    return owner_id;\n}\n\n// TestKeyOwner(..., ID)   : (owner == None || owner == ID)\n// TestKeyOwner(..., None) : (owner == None)\n// TestKeyOwner(..., Any)  : no owner test\n// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags.\nbool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id)\n{\n    if (!IsNamedKeyOrModKey(key))\n        return true;\n\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any)\n        if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END)\n            return false;\n\n    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);\n    if (owner_id == ImGuiKeyOwner_Any)\n        return (owner_data->LockThisFrame == false);\n\n    // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId\n    // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things.\n    // Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions.\n    if (owner_data->OwnerCurr != owner_id)\n    {\n        if (owner_data->LockThisFrame)\n            return false;\n        if (owner_data->OwnerCurr != ImGuiKeyOwner_None)\n            return false;\n    }\n\n    return true;\n}\n\n// _LockXXX flags are useful to lock keys away from code which is not input-owner aware.\n// When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone.\n// - SetKeyOwner(..., None)              : clears owner\n// - SetKeyOwner(..., Any, !Lock)        : illegal (assert)\n// - SetKeyOwner(..., Any or None, Lock) : set lock\nvoid ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags)\n{\n    IM_ASSERT(IsNamedKeyOrModKey(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it)\n    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function!\n\n    ImGuiContext& g = *GImGui;\n    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);\n    owner_data->OwnerCurr = owner_data->OwnerNext = owner_id;\n\n    // We cannot lock by default as it would likely break lots of legacy code.\n    // In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test)\n    owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0;\n    owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease);\n}\n\n// Rarely used helper\nvoid ImGui::SetKeyOwnersForKeyChord(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)\n{\n    if (key_chord & ImGuiMod_Ctrl)      { SetKeyOwner(ImGuiMod_Ctrl, owner_id, flags); }\n    if (key_chord & ImGuiMod_Shift)     { SetKeyOwner(ImGuiMod_Shift, owner_id, flags); }\n    if (key_chord & ImGuiMod_Alt)       { SetKeyOwner(ImGuiMod_Alt, owner_id, flags); }\n    if (key_chord & ImGuiMod_Super)     { SetKeyOwner(ImGuiMod_Super, owner_id, flags); }\n    if (key_chord & ImGuiMod_Shortcut)  { SetKeyOwner(ImGuiMod_Shortcut, owner_id, flags); }\n    if (key_chord & ~ImGuiMod_Mask_)    { SetKeyOwner((ImGuiKey)(key_chord & ~ImGuiMod_Mask_), owner_id, flags); }\n}\n\n// This is more or less equivalent to:\n//   if (IsItemHovered() || IsItemActive())\n//       SetKeyOwner(key, GetItemID());\n// Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times.\n// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition.\n// Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority.\nvoid ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiID id = g.LastItemData.ID;\n    if (id == 0 || (g.HoveredId != id && g.ActiveId != id))\n        return;\n    if ((flags & ImGuiInputFlags_CondMask_) == 0)\n        flags |= ImGuiInputFlags_CondDefault_;\n    if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive)))\n    {\n        IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function!\n        SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_);\n    }\n}\n\n// This is the only public API until we expose owner_id versions of the API as replacements.\nbool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord)\n{\n    return IsKeyChordPressed(key_chord, 0, ImGuiInputFlags_None);\n}\n\n// This is equivalent to comparing KeyMods + doing a IsKeyPressed()\nbool ImGui::IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (key_chord & ImGuiMod_Shortcut)\n        key_chord = ConvertShortcutMod(key_chord);\n    ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_);\n    if (g.IO.KeyMods != mods)\n        return false;\n\n    // Special storage location for mods\n    ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_);\n    if (key == ImGuiKey_None)\n        key = ConvertSingleModFlagToKey(&g, mods);\n    if (!IsKeyPressed(key, owner_id, (flags & (ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateMask_))))\n        return false;\n    return true;\n}\n\nbool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags)\n{\n    // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any.\n    if ((flags & ImGuiInputFlags_RouteMask_) == 0)\n        flags |= ImGuiInputFlags_RouteFocused;\n    if (!SetShortcutRouting(key_chord, owner_id, flags))\n        return false;\n\n    if (!IsKeyChordPressed(key_chord, owner_id, flags))\n        return false;\n    IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function!\n    return true;\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] ERROR CHECKING\n//-----------------------------------------------------------------------------\n\n// Helper function to verify ABI compatibility between caller code and compiled version of Dear ImGui.\n// Verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit\n// If this triggers you have an issue:\n// - Most commonly: mismatched headers and compiled code version.\n// - Or: mismatched configuration #define, compilation settings, packing pragma etc.\n//   The configuration settings mentioned in imconfig.h must be set for all compilation units involved with Dear ImGui,\n//   which is way it is required you put them in your imconfig file (and not just before including imgui.h).\n//   Otherwise it is possible that different compilation units would see different structure layout\nbool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert, size_t sz_idx)\n{\n    bool error = false;\n    if (strcmp(version, IMGUI_VERSION) != 0) { error = true; IM_ASSERT(strcmp(version, IMGUI_VERSION) == 0 && \"Mismatched version string!\"); }\n    if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && \"Mismatched struct layout!\"); }\n    if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && \"Mismatched struct layout!\"); }\n    if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && \"Mismatched struct layout!\"); }\n    if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && \"Mismatched struct layout!\"); }\n    if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && \"Mismatched struct layout!\"); }\n    if (sz_idx != sizeof(ImDrawIdx)) { error = true; IM_ASSERT(sz_idx == sizeof(ImDrawIdx) && \"Mismatched struct layout!\"); }\n    return !error;\n}\n\n// Until 1.89 (IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos() to extend the boundary of a parent (e.g. window or table cell)\n// This is causing issues and ambiguity and we need to retire that.\n// See https://github.com/ocornut/imgui/issues/5548 for more details.\n// [Scenario 1]\n//  Previously this would make the window content size ~200x200:\n//    Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End();  // NOT OK\n//  Instead, please submit an item:\n//    Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK\n//  Alternative:\n//    Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK\n// [Scenario 2]\n//  For reference this is one of the issue what we aim to fix with this change:\n//    BeginGroup() + SomeItem(\"foobar\") + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup()\n//  The previous logic made SetCursorScreenPos(GetCursorScreenPos()) have a side-effect! It would erroneously incorporate ItemSpacing.y after the item into content size, making the group taller!\n//  While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect. Using vertical alignment patterns could trigger this issue.\nvoid ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(window->DC.IsSetPos);\n    window->DC.IsSetPos = false;\n#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y)\n        return;\n    if (window->SkipItems)\n        return;\n    IM_ASSERT(0 && \"Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries. Please submit an item e.g. Dummy() to validate extent.\");\n#else\n    window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);\n#endif\n}\n\nstatic void ImGui::ErrorCheckNewFrameSanityChecks()\n{\n    ImGuiContext& g = *GImGui;\n\n    // Check user IM_ASSERT macro\n    // (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means your assert macro is incorrectly defined!\n    //  If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.\n    //  This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)\n    // #define IM_ASSERT(EXPR)   if (SomeCode(EXPR)) SomeMoreCode();                    // Wrong!\n    // #define IM_ASSERT(EXPR)   do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0)   // Correct!\n    if (true) IM_ASSERT(1); else IM_ASSERT(0);\n\n    // Emscripten backends are often imprecise in their submission of DeltaTime. (#6114, #3644)\n    // Ideally the Emscripten app/backend should aim to fix or smooth this value and avoid feeding zero, but we tolerate it.\n#ifdef __EMSCRIPTEN__\n    if (g.IO.DeltaTime <= 0.0f && g.FrameCount > 0)\n        g.IO.DeltaTime = 0.00001f;\n#endif\n\n    // Check user data\n    // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument)\n    IM_ASSERT(g.Initialized);\n    IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0)              && \"Need a positive DeltaTime!\");\n    IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount)  && \"Forgot to call Render() or EndFrame() at the end of the previous frame?\");\n    IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f  && \"Invalid DisplaySize value!\");\n    IM_ASSERT(g.IO.Fonts->IsBuilt()                                     && \"Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()\");\n    IM_ASSERT(g.Style.CurveTessellationTol > 0.0f                       && \"Invalid style setting!\");\n    IM_ASSERT(g.Style.CircleTessellationMaxError > 0.0f                 && \"Invalid style setting!\");\n    IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f            && \"Invalid style setting!\"); // Allows us to avoid a few clamps in color computations\n    IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && \"Invalid style setting.\");\n    IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);\n    IM_ASSERT(g.Style.ColorButtonPosition == ImGuiDir_Left || g.Style.ColorButtonPosition == ImGuiDir_Right);\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_COUNT; n++)\n        IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < ImGuiKey_LegacyNativeKey_END && \"io.KeyMap[] contains an out of bound value (need to be 0..511, or -1 for unmapped key)\");\n\n    // Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP)\n    if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && g.IO.BackendUsingLegacyKeyArrays == 1)\n        IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && \"ImGuiKey_Space is not mapped, required for keyboard navigation.\");\n#endif\n\n    // Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.\n    if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))\n        g.IO.ConfigWindowsResizeFromEdges = false;\n}\n\nstatic void ImGui::ErrorCheckEndFrameSanityChecks()\n{\n    ImGuiContext& g = *GImGui;\n\n    // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame()\n    // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame().\n    // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will\n    // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs.\n    // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0),\n    // while still correctly asserting on mid-frame key press events.\n    const ImGuiKeyChord key_mods = GetMergedModsFromKeys();\n    IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && \"Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods\");\n    IM_UNUSED(key_mods);\n\n    // [EXPERIMENTAL] Recover from errors: You may call this yourself before EndFrame().\n    //ErrorCheckEndFrameRecover();\n\n    // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you\n    // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).\n    if (g.CurrentWindowStack.Size != 1)\n    {\n        if (g.CurrentWindowStack.Size > 1)\n        {\n            ImGuiWindow* window = g.CurrentWindowStack.back().Window; // <-- This window was not Ended!\n            IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, \"Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?\");\n            IM_UNUSED(window);\n            while (g.CurrentWindowStack.Size > 1)\n                End();\n        }\n        else\n        {\n            IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, \"Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?\");\n        }\n    }\n\n    IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, \"Missing EndGroup call!\");\n}\n\n// Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.\n// Must be called during or before EndFrame().\n// This is generally flawed as we are not necessarily End/Popping things in the right order.\n// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.\n// FIXME: Can't recover from interleaved BeginTabBar/Begin\nvoid    ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)\n{\n    // PVS-Studio V1044 is \"Loop break conditions do not depend on the number of iterations\"\n    ImGuiContext& g = *GImGui;\n    while (g.CurrentWindowStack.Size > 0) //-V1044\n    {\n        ErrorCheckEndWindowRecover(log_callback, user_data);\n        ImGuiWindow* window = g.CurrentWindow;\n        if (g.CurrentWindowStack.Size == 1)\n        {\n            IM_ASSERT(window->IsFallbackWindow);\n            break;\n        }\n        if (window->Flags & ImGuiWindowFlags_ChildWindow)\n        {\n            if (log_callback) log_callback(user_data, \"Recovered from missing EndChild() for '%s'\", window->Name);\n            EndChild();\n        }\n        else\n        {\n            if (log_callback) log_callback(user_data, \"Recovered from missing End() for '%s'\", window->Name);\n            End();\n        }\n    }\n}\n\n// Must be called before End()/EndChild()\nvoid    ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data)\n{\n    ImGuiContext& g = *GImGui;\n    while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))\n    {\n        if (log_callback) log_callback(user_data, \"Recovered from missing EndTable() in '%s'\", g.CurrentTable->OuterWindow->Name);\n        EndTable();\n    }\n\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin;\n    IM_ASSERT(window != NULL);\n    while (g.CurrentTabBar != NULL) //-V1044\n    {\n        if (log_callback) log_callback(user_data, \"Recovered from missing EndTabBar() in '%s'\", window->Name);\n        EndTabBar();\n    }\n    while (window->DC.TreeDepth > 0)\n    {\n        if (log_callback) log_callback(user_data, \"Recovered from missing TreePop() in '%s'\", window->Name);\n        TreePop();\n    }\n    while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044\n    {\n        if (log_callback) log_callback(user_data, \"Recovered from missing EndGroup() in '%s'\", window->Name);\n        EndGroup();\n    }\n    while (window->IDStack.Size > 1)\n    {\n        if (log_callback) log_callback(user_data, \"Recovered from missing PopID() in '%s'\", window->Name);\n        PopID();\n    }\n    while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044\n    {\n        if (log_callback) log_callback(user_data, \"Recovered from missing EndDisabled() in '%s'\", window->Name);\n        EndDisabled();\n    }\n    while (g.ColorStack.Size > stack_sizes->SizeOfColorStack)\n    {\n        if (log_callback) log_callback(user_data, \"Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s\", window->Name, GetStyleColorName(g.ColorStack.back().Col));\n        PopStyleColor();\n    }\n    while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044\n    {\n        if (log_callback) log_callback(user_data, \"Recovered from missing PopItemFlag() in '%s'\", window->Name);\n        PopItemFlag();\n    }\n    while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044\n    {\n        if (log_callback) log_callback(user_data, \"Recovered from missing PopStyleVar() in '%s'\", window->Name);\n        PopStyleVar();\n    }\n    while (g.FontStack.Size > stack_sizes->SizeOfFontStack) //-V1044\n    {\n        if (log_callback) log_callback(user_data, \"Recovered from missing PopFont() in '%s'\", window->Name);\n        PopFont();\n    }\n    while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack + 1) //-V1044\n    {\n        if (log_callback) log_callback(user_data, \"Recovered from missing PopFocusScope() in '%s'\", window->Name);\n        PopFocusScope();\n    }\n}\n\n// Save current stack sizes for later compare\nvoid ImGuiStackSizes::SetToContextState(ImGuiContext* ctx)\n{\n    ImGuiContext& g = *ctx;\n    ImGuiWindow* window = g.CurrentWindow;\n    SizeOfIDStack = (short)window->IDStack.Size;\n    SizeOfColorStack = (short)g.ColorStack.Size;\n    SizeOfStyleVarStack = (short)g.StyleVarStack.Size;\n    SizeOfFontStack = (short)g.FontStack.Size;\n    SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size;\n    SizeOfGroupStack = (short)g.GroupStack.Size;\n    SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size;\n    SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size;\n    SizeOfDisabledStack = (short)g.DisabledStackSize;\n}\n\n// Compare to detect usage errors\nvoid ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx)\n{\n    ImGuiContext& g = *ctx;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_UNUSED(window);\n\n    // Window stacks\n    // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin)\n    IM_ASSERT(SizeOfIDStack         == window->IDStack.Size     && \"PushID/PopID or TreeNode/TreePop Mismatch!\");\n\n    // Global stacks\n    // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them.\n    IM_ASSERT(SizeOfGroupStack      == g.GroupStack.Size        && \"BeginGroup/EndGroup Mismatch!\");\n    IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size   && \"BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!\");\n    IM_ASSERT(SizeOfDisabledStack   == g.DisabledStackSize      && \"BeginDisabled/EndDisabled Mismatch!\");\n    IM_ASSERT(SizeOfItemFlagsStack  >= g.ItemFlagsStack.Size    && \"PushItemFlag/PopItemFlag Mismatch!\");\n    IM_ASSERT(SizeOfColorStack      >= g.ColorStack.Size        && \"PushStyleColor/PopStyleColor Mismatch!\");\n    IM_ASSERT(SizeOfStyleVarStack   >= g.StyleVarStack.Size     && \"PushStyleVar/PopStyleVar Mismatch!\");\n    IM_ASSERT(SizeOfFontStack       >= g.FontStack.Size         && \"PushFont/PopFont Mismatch!\");\n    IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size   && \"PushFocusScope/PopFocusScope Mismatch!\");\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] LAYOUT\n//-----------------------------------------------------------------------------\n// - ItemSize()\n// - ItemAdd()\n// - SameLine()\n// - GetCursorScreenPos()\n// - SetCursorScreenPos()\n// - GetCursorPos(), GetCursorPosX(), GetCursorPosY()\n// - SetCursorPos(), SetCursorPosX(), SetCursorPosY()\n// - GetCursorStartPos()\n// - Indent()\n// - Unindent()\n// - SetNextItemWidth()\n// - PushItemWidth()\n// - PushMultiItemsWidths()\n// - PopItemWidth()\n// - CalcItemWidth()\n// - CalcItemSize()\n// - GetTextLineHeight()\n// - GetTextLineHeightWithSpacing()\n// - GetFrameHeight()\n// - GetFrameHeightWithSpacing()\n// - GetContentRegionMax()\n// - GetContentRegionMaxAbs() [Internal]\n// - GetContentRegionAvail(),\n// - GetWindowContentRegionMin(), GetWindowContentRegionMax()\n// - BeginGroup()\n// - EndGroup()\n// Also see in imgui_widgets: tab bars, and in imgui_tables: tables, columns.\n//-----------------------------------------------------------------------------\n\n// Advance cursor given item size for layout.\n// Register minimum needed size so it can extend the bounding box used for auto-fit calculation.\n// See comments in ItemAdd() about how/why the size provided to ItemSize() vs ItemAdd() may often different.\nvoid ImGui::ItemSize(const ImVec2& size, float text_baseline_y)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    // We increase the height in this function to accommodate for baseline offset.\n    // In theory we should be offsetting the starting position (window->DC.CursorPos), that will be the topic of a larger refactor,\n    // but since ItemSize() is not yet an API that moves the cursor (to handle e.g. wrapping) enlarging the height has the same effect.\n    const float offset_to_match_baseline_y = (text_baseline_y >= 0) ? ImMax(0.0f, window->DC.CurrLineTextBaseOffset - text_baseline_y) : 0.0f;\n\n    const float line_y1 = window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y;\n    const float line_height = ImMax(window->DC.CurrLineSize.y, /*ImMax(*/window->DC.CursorPos.y - line_y1/*, 0.0f)*/ + size.y + offset_to_match_baseline_y);\n\n    // Always align ourselves on pixel boundaries\n    //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG]\n    window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x;\n    window->DC.CursorPosPrevLine.y = line_y1;\n    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);    // Next line\n    window->DC.CursorPos.y = IM_TRUNC(line_y1 + line_height + g.Style.ItemSpacing.y);                       // Next line\n    window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x);\n    window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y);\n    //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG]\n\n    window->DC.PrevLineSize.y = line_height;\n    window->DC.CurrLineSize.y = 0.0f;\n    window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y);\n    window->DC.CurrLineTextBaseOffset = 0.0f;\n    window->DC.IsSameLine = window->DC.IsSetPos = false;\n\n    // Horizontal layout mode\n    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)\n        SameLine();\n}\n\n// Declare item bounding box for clipping and interaction.\n// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface\n// declare their minimum size requirement to ItemSize() and provide a larger region to ItemAdd() which is used drawing/interaction.\nbool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGuiItemFlags extra_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Set item data\n    // (DisplayRect is left untouched, made valid when ImGuiItemStatusFlags_HasDisplayRect is set)\n    g.LastItemData.ID = id;\n    g.LastItemData.Rect = bb;\n    g.LastItemData.NavRect = nav_bb_arg ? *nav_bb_arg : bb;\n    g.LastItemData.InFlags = g.CurrentItemFlags | g.NextItemData.ItemFlags | extra_flags;\n    g.LastItemData.StatusFlags = ImGuiItemStatusFlags_None;\n    // Note: we don't copy 'g.NextItemData.SelectionUserData' to an hypothetical g.LastItemData.SelectionUserData: since the former is not cleared.\n\n    // Directional navigation processing\n    if (id != 0)\n    {\n        KeepAliveID(id);\n\n        // Runs prior to clipping early-out\n        //  (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget\n        //  (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests\n        //      unfortunately, but it is still limited to one window. It may not scale very well for windows with ten of\n        //      thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame.\n        //      We could early out with \"if (is_clipped && !g.NavInitRequest) return false;\" but when we wouldn't be able\n        //      to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick).\n        // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null.\n        // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere.\n        if (!(g.LastItemData.InFlags & ImGuiItemFlags_NoNav))\n        {\n            window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent);\n            if (g.NavId == id || g.NavAnyRequest)\n                if (g.NavWindow->RootWindowForNav == window->RootWindowForNav)\n                    if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened))\n                        NavProcessItem();\n        }\n\n        // [DEBUG] People keep stumbling on this problem and using \"\" as identifier in the root of a window instead of \"##something\".\n        // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use \"##something\".\n        // READ THE FAQ: https://dearimgui.com/faq\n        IM_ASSERT(id != window->ID && \"Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!\");\n    }\n    g.NextItemData.Flags = ImGuiNextItemDataFlags_None;\n    g.NextItemData.ItemFlags = ImGuiItemFlags_None;\n\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    if (id != 0)\n        IMGUI_TEST_ENGINE_ITEM_ADD(id, g.LastItemData.NavRect, &g.LastItemData);\n#endif\n\n    // Clipping test\n    // (FIXME: This is a modified copy of IsClippedEx() so we can reuse the is_rect_visible value)\n    //const bool is_clipped = IsClippedEx(bb, id);\n    //if (is_clipped)\n    //    return false;\n    const bool is_rect_visible = bb.Overlaps(window->ClipRect);\n    if (!is_rect_visible)\n        if (id == 0 || (id != g.ActiveId && id != g.ActiveIdPreviousFrame && id != g.NavId))\n            if (!g.LogEnabled)\n                return false;\n\n    // [DEBUG]\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    if (id != 0 && id == g.DebugLocateId)\n        DebugLocateItemResolveWithLastItem();\n#endif\n    //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG]\n    //if ((g.LastItemData.InFlags & ImGuiItemFlags_NoNav) == 0)\n    //    window->DrawList->AddRect(g.LastItemData.NavRect.Min, g.LastItemData.NavRect.Max, IM_COL32(255,255,0,255)); // [DEBUG]\n\n    // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them)\n    if (is_rect_visible)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible;\n    if (IsMouseHoveringRect(bb.Min, bb.Max))\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect;\n    return true;\n}\n\n// Gets back to previous line and continue with horizontal layout\n//      offset_from_start_x == 0 : follow right after previous item\n//      offset_from_start_x != 0 : align to specified x position (relative to window/group left)\n//      spacing_w < 0            : use default spacing if offset_from_start_x == 0, no spacing if offset_from_start_x != 0\n//      spacing_w >= 0           : enforce spacing amount\nvoid ImGui::SameLine(float offset_from_start_x, float spacing_w)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    if (offset_from_start_x != 0.0f)\n    {\n        if (spacing_w < 0.0f)\n            spacing_w = 0.0f;\n        window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + offset_from_start_x + spacing_w + window->DC.GroupOffset.x + window->DC.ColumnsOffset.x;\n        window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;\n    }\n    else\n    {\n        if (spacing_w < 0.0f)\n            spacing_w = g.Style.ItemSpacing.x;\n        window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w;\n        window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y;\n    }\n    window->DC.CurrLineSize = window->DC.PrevLineSize;\n    window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;\n    window->DC.IsSameLine = true;\n}\n\nImVec2 ImGui::GetCursorScreenPos()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CursorPos;\n}\n\nvoid ImGui::SetCursorScreenPos(const ImVec2& pos)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.CursorPos = pos;\n    //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);\n    window->DC.IsSetPos = true;\n}\n\n// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient.\n// Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'.\nImVec2 ImGui::GetCursorPos()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CursorPos - window->Pos + window->Scroll;\n}\n\nfloat ImGui::GetCursorPosX()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x;\n}\n\nfloat ImGui::GetCursorPosY()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y;\n}\n\nvoid ImGui::SetCursorPos(const ImVec2& local_pos)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.CursorPos = window->Pos - window->Scroll + local_pos;\n    //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos);\n    window->DC.IsSetPos = true;\n}\n\nvoid ImGui::SetCursorPosX(float x)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x;\n    //window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x);\n    window->DC.IsSetPos = true;\n}\n\nvoid ImGui::SetCursorPosY(float y)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y;\n    //window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y);\n    window->DC.IsSetPos = true;\n}\n\nImVec2 ImGui::GetCursorStartPos()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CursorStartPos - window->Pos;\n}\n\nvoid ImGui::Indent(float indent_w)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.Indent.x += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;\n    window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;\n}\n\nvoid ImGui::Unindent(float indent_w)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.Indent.x -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing;\n    window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;\n}\n\n// Affect large frame+labels widgets only.\nvoid ImGui::SetNextItemWidth(float item_width)\n{\n    ImGuiContext& g = *GImGui;\n    g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasWidth;\n    g.NextItemData.Width = item_width;\n}\n\n// FIXME: Remove the == 0.0f behavior?\nvoid ImGui::PushItemWidth(float item_width)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width\n    window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width);\n    g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;\n}\n\nvoid ImGui::PushMultiItemsWidths(int components, float w_full)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    const ImGuiStyle& style = g.Style;\n    const float w_item_one  = ImMax(1.0f, IM_TRUNC((w_full - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));\n    const float w_item_last = ImMax(1.0f, IM_TRUNC(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));\n    window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); // Backup current width\n    window->DC.ItemWidthStack.push_back(w_item_last);\n    for (int i = 0; i < components - 2; i++)\n        window->DC.ItemWidthStack.push_back(w_item_one);\n    window->DC.ItemWidth = (components == 1) ? w_item_last : w_item_one;\n    g.NextItemData.Flags &= ~ImGuiNextItemDataFlags_HasWidth;\n}\n\nvoid ImGui::PopItemWidth()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    window->DC.ItemWidth = window->DC.ItemWidthStack.back();\n    window->DC.ItemWidthStack.pop_back();\n}\n\n// Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth().\n// The SetNextItemWidth() data is generally cleared/consumed by ItemAdd() or NextItemData.ClearFlags()\nfloat ImGui::CalcItemWidth()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    float w;\n    if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)\n        w = g.NextItemData.Width;\n    else\n        w = window->DC.ItemWidth;\n    if (w < 0.0f)\n    {\n        float region_max_x = GetContentRegionMaxAbs().x;\n        w = ImMax(1.0f, region_max_x - window->DC.CursorPos.x + w);\n    }\n    w = IM_TRUNC(w);\n    return w;\n}\n\n// [Internal] Calculate full item size given user provided 'size' parameter and default width/height. Default width is often == CalcItemWidth().\n// Those two functions CalcItemWidth vs CalcItemSize are awkwardly named because they are not fully symmetrical.\n// Note that only CalcItemWidth() is publicly exposed.\n// The 4.0f here may be changed to match CalcItemWidth() and/or BeginChild() (right now we have a mismatch which is harmless but undesirable)\nImVec2 ImGui::CalcItemSize(ImVec2 size, float default_w, float default_h)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    ImVec2 region_max;\n    if (size.x < 0.0f || size.y < 0.0f)\n        region_max = GetContentRegionMaxAbs();\n\n    if (size.x == 0.0f)\n        size.x = default_w;\n    else if (size.x < 0.0f)\n        size.x = ImMax(4.0f, region_max.x - window->DC.CursorPos.x + size.x);\n\n    if (size.y == 0.0f)\n        size.y = default_h;\n    else if (size.y < 0.0f)\n        size.y = ImMax(4.0f, region_max.y - window->DC.CursorPos.y + size.y);\n\n    return size;\n}\n\nfloat ImGui::GetTextLineHeight()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize;\n}\n\nfloat ImGui::GetTextLineHeightWithSpacing()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize + g.Style.ItemSpacing.y;\n}\n\nfloat ImGui::GetFrameHeight()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize + g.Style.FramePadding.y * 2.0f;\n}\n\nfloat ImGui::GetFrameHeightWithSpacing()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y;\n}\n\n// FIXME: All the Contents Region function are messy or misleading. WE WILL AIM TO OBSOLETE ALL OF THEM WITH A NEW \"WORK RECT\" API. Thanks for your patience!\n\n// FIXME: This is in window space (not screen space!).\nImVec2 ImGui::GetContentRegionMax()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;\n    return mx - window->Pos;\n}\n\n// [Internal] Absolute coordinate. Saner. This is not exposed until we finishing refactoring work rect features.\nImVec2 ImGui::GetContentRegionMaxAbs()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImVec2 mx = (window->DC.CurrentColumns || g.CurrentTable) ? window->WorkRect.Max : window->ContentRegionRect.Max;\n    return mx;\n}\n\nImVec2 ImGui::GetContentRegionAvail()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return GetContentRegionMaxAbs() - window->DC.CursorPos;\n}\n\n// In window space (not screen space!)\nImVec2 ImGui::GetWindowContentRegionMin()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ContentRegionRect.Min - window->Pos;\n}\n\nImVec2 ImGui::GetWindowContentRegionMax()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ContentRegionRect.Max - window->Pos;\n}\n\n// Lock horizontal starting position + capture group bounding box into one \"item\" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)\n// Groups are currently a mishmash of functionalities which should perhaps be clarified and separated.\n// FIXME-OPT: Could we safely early out on ->SkipItems?\nvoid ImGui::BeginGroup()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    g.GroupStack.resize(g.GroupStack.Size + 1);\n    ImGuiGroupData& group_data = g.GroupStack.back();\n    group_data.WindowID = window->ID;\n    group_data.BackupCursorPos = window->DC.CursorPos;\n    group_data.BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;\n    group_data.BackupCursorMaxPos = window->DC.CursorMaxPos;\n    group_data.BackupIndent = window->DC.Indent;\n    group_data.BackupGroupOffset = window->DC.GroupOffset;\n    group_data.BackupCurrLineSize = window->DC.CurrLineSize;\n    group_data.BackupCurrLineTextBaseOffset = window->DC.CurrLineTextBaseOffset;\n    group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive;\n    group_data.BackupHoveredIdIsAlive = g.HoveredId != 0;\n    group_data.BackupIsSameLine = window->DC.IsSameLine;\n    group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive;\n    group_data.EmitItem = true;\n\n    window->DC.GroupOffset.x = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffset.x;\n    window->DC.Indent = window->DC.GroupOffset;\n    window->DC.CursorMaxPos = window->DC.CursorPos;\n    window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);\n    if (g.LogEnabled)\n        g.LogLinePosY = -FLT_MAX; // To enforce a carriage return\n}\n\nvoid ImGui::EndGroup()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(g.GroupStack.Size > 0); // Mismatched BeginGroup()/EndGroup() calls\n\n    ImGuiGroupData& group_data = g.GroupStack.back();\n    IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window?\n\n    if (window->DC.IsSetPos)\n        ErrorCheckUsingSetCursorPosToExtendParentBoundaries();\n\n    ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos));\n\n    window->DC.CursorPos = group_data.BackupCursorPos;\n    window->DC.CursorPosPrevLine = group_data.BackupCursorPosPrevLine;\n    window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos);\n    window->DC.Indent = group_data.BackupIndent;\n    window->DC.GroupOffset = group_data.BackupGroupOffset;\n    window->DC.CurrLineSize = group_data.BackupCurrLineSize;\n    window->DC.CurrLineTextBaseOffset = group_data.BackupCurrLineTextBaseOffset;\n    window->DC.IsSameLine = group_data.BackupIsSameLine;\n    if (g.LogEnabled)\n        g.LogLinePosY = -FLT_MAX; // To enforce a carriage return\n\n    if (!group_data.EmitItem)\n    {\n        g.GroupStack.pop_back();\n        return;\n    }\n\n    window->DC.CurrLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrLineTextBaseOffset);      // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now.\n    ItemSize(group_bb.GetSize());\n    ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop);\n\n    // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group.\n    // It would be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets.\n    // Also if you grep for LastItemId you'll notice it is only used in that context.\n    // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.)\n    const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId;\n    const bool group_contains_prev_active_id = (group_data.BackupActiveIdPreviousFrameIsAlive == false) && (g.ActiveIdPreviousFrameIsAlive == true);\n    if (group_contains_curr_active_id)\n        g.LastItemData.ID = g.ActiveId;\n    else if (group_contains_prev_active_id)\n        g.LastItemData.ID = g.ActiveIdPreviousFrame;\n    g.LastItemData.Rect = group_bb;\n\n    // Forward Hovered flag\n    const bool group_contains_curr_hovered_id = (group_data.BackupHoveredIdIsAlive == false) && g.HoveredId != 0;\n    if (group_contains_curr_hovered_id)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;\n\n    // Forward Edited flag\n    if (group_contains_curr_active_id && g.ActiveIdHasBeenEditedThisFrame)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Edited;\n\n    // Forward Deactivated flag\n    g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDeactivated;\n    if (group_contains_prev_active_id && g.ActiveId != g.ActiveIdPreviousFrame)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Deactivated;\n\n    g.GroupStack.pop_back();\n    if (g.DebugShowGroupRects)\n        window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255));   // [Debug]\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] SCROLLING\n//-----------------------------------------------------------------------------\n\n// Helper to snap on edges when aiming at an item very close to the edge,\n// So the difference between WindowPadding and ItemSpacing will be in the visible area after scrolling.\n// When we refactor the scrolling API this may be configurable with a flag?\n// Note that the effect for this won't be visible on X axis with default Style settings as WindowPadding.x == ItemSpacing.x by default.\nstatic float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, float snap_threshold, float center_ratio)\n{\n    if (target <= snap_min + snap_threshold)\n        return ImLerp(snap_min, target, center_ratio);\n    if (target >= snap_max - snap_threshold)\n        return ImLerp(target, snap_max, center_ratio);\n    return target;\n}\n\nstatic ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window)\n{\n    ImVec2 scroll = window->Scroll;\n    ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2);\n    for (int axis = 0; axis < 2; axis++)\n    {\n        if (window->ScrollTarget[axis] < FLT_MAX)\n        {\n            float center_ratio = window->ScrollTargetCenterRatio[axis];\n            float scroll_target = window->ScrollTarget[axis];\n            if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f)\n            {\n                float snap_min = 0.0f;\n                float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis];\n                scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio);\n            }\n            scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]);\n        }\n        scroll[axis] = IM_TRUNC(ImMax(scroll[axis], 0.0f));\n        if (!window->Collapsed && !window->SkipItems)\n            scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]);\n    }\n    return scroll;\n}\n\nvoid ImGui::ScrollToItem(ImGuiScrollFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ScrollToRectEx(window, g.LastItemData.NavRect, flags);\n}\n\nvoid ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)\n{\n    ScrollToRectEx(window, item_rect, flags);\n}\n\n// Scroll to keep newly navigated item fully into view\nImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1));\n    scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x);\n    scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y);\n    //GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG]\n    //GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG]\n\n    // Check that only one behavior is selected per axis\n    IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_));\n    IM_ASSERT((flags & ImGuiScrollFlags_MaskY_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskY_));\n\n    // Defaults\n    ImGuiScrollFlags in_flags = flags;\n    if ((flags & ImGuiScrollFlags_MaskX_) == 0 && window->ScrollbarX)\n        flags |= ImGuiScrollFlags_KeepVisibleEdgeX;\n    if ((flags & ImGuiScrollFlags_MaskY_) == 0)\n        flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY;\n\n    const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x;\n    const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y;\n    const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= scroll_rect.GetWidth() || (window->AutoFitFramesX > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;\n    const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= scroll_rect.GetHeight() || (window->AutoFitFramesY > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0;\n\n    if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x)\n    {\n        if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x)\n            SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f);\n        else if (item_rect.Max.x >= scroll_rect.Max.x)\n            SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f);\n    }\n    else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX))\n    {\n        if (can_be_fully_visible_x)\n            SetScrollFromPosX(window, ImTrunc((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f);\n        else\n            SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f);\n    }\n\n    if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y)\n    {\n        if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y)\n            SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f);\n        else if (item_rect.Max.y >= scroll_rect.Max.y)\n            SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f);\n    }\n    else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY))\n    {\n        if (can_be_fully_visible_y)\n            SetScrollFromPosY(window, ImTrunc((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f);\n        else\n            SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f);\n    }\n\n    ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);\n    ImVec2 delta_scroll = next_scroll - window->Scroll;\n\n    // Also scroll parent window to keep us into view if necessary\n    if (!(flags & ImGuiScrollFlags_NoScrollParent) && (window->Flags & ImGuiWindowFlags_ChildWindow))\n    {\n        // FIXME-SCROLL: May be an option?\n        if ((in_flags & (ImGuiScrollFlags_AlwaysCenterX | ImGuiScrollFlags_KeepVisibleCenterX)) != 0)\n            in_flags = (in_flags & ~ImGuiScrollFlags_MaskX_) | ImGuiScrollFlags_KeepVisibleEdgeX;\n        if ((in_flags & (ImGuiScrollFlags_AlwaysCenterY | ImGuiScrollFlags_KeepVisibleCenterY)) != 0)\n            in_flags = (in_flags & ~ImGuiScrollFlags_MaskY_) | ImGuiScrollFlags_KeepVisibleEdgeY;\n        delta_scroll += ScrollToRectEx(window->ParentWindow, ImRect(item_rect.Min - delta_scroll, item_rect.Max - delta_scroll), in_flags);\n    }\n\n    return delta_scroll;\n}\n\nfloat ImGui::GetScrollX()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->Scroll.x;\n}\n\nfloat ImGui::GetScrollY()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->Scroll.y;\n}\n\nfloat ImGui::GetScrollMaxX()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ScrollMax.x;\n}\n\nfloat ImGui::GetScrollMaxY()\n{\n    ImGuiWindow* window = GImGui->CurrentWindow;\n    return window->ScrollMax.y;\n}\n\nvoid ImGui::SetScrollX(ImGuiWindow* window, float scroll_x)\n{\n    window->ScrollTarget.x = scroll_x;\n    window->ScrollTargetCenterRatio.x = 0.0f;\n    window->ScrollTargetEdgeSnapDist.x = 0.0f;\n}\n\nvoid ImGui::SetScrollY(ImGuiWindow* window, float scroll_y)\n{\n    window->ScrollTarget.y = scroll_y;\n    window->ScrollTargetCenterRatio.y = 0.0f;\n    window->ScrollTargetEdgeSnapDist.y = 0.0f;\n}\n\nvoid ImGui::SetScrollX(float scroll_x)\n{\n    ImGuiContext& g = *GImGui;\n    SetScrollX(g.CurrentWindow, scroll_x);\n}\n\nvoid ImGui::SetScrollY(float scroll_y)\n{\n    ImGuiContext& g = *GImGui;\n    SetScrollY(g.CurrentWindow, scroll_y);\n}\n\n// Note that a local position will vary depending on initial scroll value,\n// This is a little bit confusing so bear with us:\n//  - local_pos = (absolution_pos - window->Pos)\n//  - So local_x/local_y are 0.0f for a position at the upper-left corner of a window,\n//    and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area.\n//  - They mostly exist because of legacy API.\n// Following the rules above, when trying to work with scrolling code, consider that:\n//  - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect!\n//  - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense\n// We store a target position so centering and clamping can occur on the next frame when we are guaranteed to have a known window size\nvoid ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio)\n{\n    IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f);\n    window->ScrollTarget.x = IM_TRUNC(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset\n    window->ScrollTargetCenterRatio.x = center_x_ratio;\n    window->ScrollTargetEdgeSnapDist.x = 0.0f;\n}\n\nvoid ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio)\n{\n    IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f);\n    window->ScrollTarget.y = IM_TRUNC(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset\n    window->ScrollTargetCenterRatio.y = center_y_ratio;\n    window->ScrollTargetEdgeSnapDist.y = 0.0f;\n}\n\nvoid ImGui::SetScrollFromPosX(float local_x, float center_x_ratio)\n{\n    ImGuiContext& g = *GImGui;\n    SetScrollFromPosX(g.CurrentWindow, local_x, center_x_ratio);\n}\n\nvoid ImGui::SetScrollFromPosY(float local_y, float center_y_ratio)\n{\n    ImGuiContext& g = *GImGui;\n    SetScrollFromPosY(g.CurrentWindow, local_y, center_y_ratio);\n}\n\n// center_x_ratio: 0.0f left of last item, 0.5f horizontal center of last item, 1.0f right of last item.\nvoid ImGui::SetScrollHereX(float center_x_ratio)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    float spacing_x = ImMax(window->WindowPadding.x, g.Style.ItemSpacing.x);\n    float target_pos_x = ImLerp(g.LastItemData.Rect.Min.x - spacing_x, g.LastItemData.Rect.Max.x + spacing_x, center_x_ratio);\n    SetScrollFromPosX(window, target_pos_x - window->Pos.x, center_x_ratio); // Convert from absolute to local pos\n\n    // Tweak: snap on edges when aiming at an item very close to the edge\n    window->ScrollTargetEdgeSnapDist.x = ImMax(0.0f, window->WindowPadding.x - spacing_x);\n}\n\n// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item.\nvoid ImGui::SetScrollHereY(float center_y_ratio)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    float spacing_y = ImMax(window->WindowPadding.y, g.Style.ItemSpacing.y);\n    float target_pos_y = ImLerp(window->DC.CursorPosPrevLine.y - spacing_y, window->DC.CursorPosPrevLine.y + window->DC.PrevLineSize.y + spacing_y, center_y_ratio);\n    SetScrollFromPosY(window, target_pos_y - window->Pos.y, center_y_ratio); // Convert from absolute to local pos\n\n    // Tweak: snap on edges when aiming at an item very close to the edge\n    window->ScrollTargetEdgeSnapDist.y = ImMax(0.0f, window->WindowPadding.y - spacing_y);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] TOOLTIPS\n//-----------------------------------------------------------------------------\n\nbool ImGui::BeginTooltip()\n{\n    return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);\n}\n\nbool ImGui::BeginItemTooltip()\n{\n    if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n        return false;\n    return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);\n}\n\nbool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)\n{\n    ImGuiContext& g = *GImGui;\n\n    if (g.DragDropWithinSource || g.DragDropWithinTarget)\n    {\n        // Drag and Drop tooltips are positioning differently than other tooltips:\n        // - offset visibility to increase visibility around mouse.\n        // - never clamp within outer viewport boundary.\n        // We call SetNextWindowPos() to enforce position and disable clamping.\n        // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones).\n        //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;\n        ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET * g.Style.MouseCursorScale;\n        SetNextWindowPos(tooltip_pos);\n        SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);\n        //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(\n        tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;\n    }\n\n    char window_name[16];\n    ImFormatString(window_name, IM_ARRAYSIZE(window_name), \"##Tooltip_%02d\", g.TooltipOverrideCount);\n    if (tooltip_flags & ImGuiTooltipFlags_OverridePrevious)\n        if (ImGuiWindow* window = FindWindowByName(window_name))\n            if (window->Active)\n            {\n                // Hide previous tooltip from being displayed. We can't easily \"reset\" the content of a window so we create a new one.\n                SetWindowHiddendAndSkipItemsForCurrentFrame(window);\n                ImFormatString(window_name, IM_ARRAYSIZE(window_name), \"##Tooltip_%02d\", ++g.TooltipOverrideCount);\n            }\n    ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize;\n    Begin(window_name, NULL, flags | extra_window_flags);\n    // 2023-03-09: Added bool return value to the API, but currently always returning true.\n    // If this ever returns false we need to update BeginDragDropSource() accordingly.\n    //if (!ret)\n    //    End();\n    //return ret;\n    return true;\n}\n\nvoid ImGui::EndTooltip()\n{\n    IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip);   // Mismatched BeginTooltip()/EndTooltip() calls\n    End();\n}\n\nvoid ImGui::SetTooltip(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    SetTooltipV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::SetTooltipV(const char* fmt, va_list args)\n{\n    if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))\n        return;\n    TextV(fmt, args);\n    EndTooltip();\n}\n\n// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.\n// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.\nvoid ImGui::SetItemTooltip(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n        SetTooltipV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::SetItemTooltipV(const char* fmt, va_list args)\n{\n    if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n        SetTooltipV(fmt, args);\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] POPUPS\n//-----------------------------------------------------------------------------\n\n// Supported flags: ImGuiPopupFlags_AnyPopupId, ImGuiPopupFlags_AnyPopupLevel\nbool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (popup_flags & ImGuiPopupFlags_AnyPopupId)\n    {\n        // Return true if any popup is open at the current BeginPopup() level of the popup stack\n        // This may be used to e.g. test for another popups already opened to handle popups priorities at the same level.\n        IM_ASSERT(id == 0);\n        if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)\n            return g.OpenPopupStack.Size > 0;\n        else\n            return g.OpenPopupStack.Size > g.BeginPopupStack.Size;\n    }\n    else\n    {\n        if (popup_flags & ImGuiPopupFlags_AnyPopupLevel)\n        {\n            // Return true if the popup is open anywhere in the popup stack\n            for (int n = 0; n < g.OpenPopupStack.Size; n++)\n                if (g.OpenPopupStack[n].PopupId == id)\n                    return true;\n            return false;\n        }\n        else\n        {\n            // Return true if the popup is open at the current BeginPopup() level of the popup stack (this is the most-common query)\n            return g.OpenPopupStack.Size > g.BeginPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].PopupId == id;\n        }\n    }\n}\n\nbool ImGui::IsPopupOpen(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiID id = (popup_flags & ImGuiPopupFlags_AnyPopupId) ? 0 : g.CurrentWindow->GetID(str_id);\n    if ((popup_flags & ImGuiPopupFlags_AnyPopupLevel) && id != 0)\n        IM_ASSERT(0 && \"Cannot use IsPopupOpen() with a string id and ImGuiPopupFlags_AnyPopupLevel.\"); // But non-string version is legal and used internally\n    return IsPopupOpen(id, popup_flags);\n}\n\n// Also see FindBlockingModal(NULL)\nImGuiWindow* ImGui::GetTopMostPopupModal()\n{\n    ImGuiContext& g = *GImGui;\n    for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)\n        if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)\n            if (popup->Flags & ImGuiWindowFlags_Modal)\n                return popup;\n    return NULL;\n}\n\n// See Demo->Stacked Modal to confirm what this is for.\nImGuiWindow* ImGui::GetTopMostAndVisiblePopupModal()\n{\n    ImGuiContext& g = *GImGui;\n    for (int n = g.OpenPopupStack.Size - 1; n >= 0; n--)\n        if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window)\n            if ((popup->Flags & ImGuiWindowFlags_Modal) && IsWindowActiveAndVisible(popup))\n                return popup;\n    return NULL;\n}\n\nvoid ImGui::OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiID id = g.CurrentWindow->GetID(str_id);\n    IMGUI_DEBUG_LOG_POPUP(\"[popup] OpenPopup(\\\"%s\\\" -> 0x%08X)\\n\", str_id, id);\n    OpenPopupEx(id, popup_flags);\n}\n\nvoid ImGui::OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags)\n{\n    OpenPopupEx(id, popup_flags);\n}\n\n// Mark popup as open (toggle toward open state).\n// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block.\n// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level).\n// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL)\nvoid ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* parent_window = g.CurrentWindow;\n    const int current_stack_size = g.BeginPopupStack.Size;\n\n    if (popup_flags & ImGuiPopupFlags_NoOpenOverExistingPopup)\n        if (IsPopupOpen((ImGuiID)0, ImGuiPopupFlags_AnyPopupId))\n            return;\n\n    ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack.\n    popup_ref.PopupId = id;\n    popup_ref.Window = NULL;\n    popup_ref.BackupNavWindow = g.NavWindow;            // When popup closes focus may be restored to NavWindow (depend on window type).\n    popup_ref.OpenFrameCount = g.FrameCount;\n    popup_ref.OpenParentId = parent_window->IDStack.back();\n    popup_ref.OpenPopupPos = NavCalcPreferredRefPos();\n    popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos;\n\n    IMGUI_DEBUG_LOG_POPUP(\"[popup] OpenPopupEx(0x%08X)\\n\", id);\n    if (g.OpenPopupStack.Size < current_stack_size + 1)\n    {\n        g.OpenPopupStack.push_back(popup_ref);\n    }\n    else\n    {\n        // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui\n        // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing\n        // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand.\n        if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1)\n        {\n            g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount;\n        }\n        else\n        {\n            // Close child popups if any, then flag popup for open/reopen\n            ClosePopupToLevel(current_stack_size, false);\n            g.OpenPopupStack.push_back(popup_ref);\n        }\n\n        // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow().\n        // This is equivalent to what ClosePopupToLevel() does.\n        //if (g.OpenPopupStack[current_stack_size].PopupId == id)\n        //    FocusWindow(parent_window);\n    }\n}\n\n// When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it.\n// This function closes any popups that are over 'ref_window'.\nvoid ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.OpenPopupStack.Size == 0)\n        return;\n\n    // Don't close our own child popup windows.\n    int popup_count_to_keep = 0;\n    if (ref_window)\n    {\n        // Find the highest popup which is a descendant of the reference window (generally reference window = NavWindow)\n        for (; popup_count_to_keep < g.OpenPopupStack.Size; popup_count_to_keep++)\n        {\n            ImGuiPopupData& popup = g.OpenPopupStack[popup_count_to_keep];\n            if (!popup.Window)\n                continue;\n            IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0);\n            if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow)\n                continue;\n\n            // Trim the stack unless the popup is a direct parent of the reference window (the reference window is often the NavWindow)\n            // - With this stack of window, clicking/focusing Popup1 will close Popup2 and Popup3:\n            //     Window -> Popup1 -> Popup2 -> Popup3\n            // - Each popups may contain child windows, which is why we compare ->RootWindow!\n            //     Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child\n            bool ref_window_is_descendent_of_popup = false;\n            for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++)\n                if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window)\n                    if (IsWindowWithinBeginStackOf(ref_window, popup_window))\n                    {\n                        ref_window_is_descendent_of_popup = true;\n                        break;\n                    }\n            if (!ref_window_is_descendent_of_popup)\n                break;\n        }\n    }\n    if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below\n    {\n        IMGUI_DEBUG_LOG_POPUP(\"[popup] ClosePopupsOverWindow(\\\"%s\\\")\\n\", ref_window ? ref_window->Name : \"<NULL>\");\n        ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup);\n    }\n}\n\nvoid ImGui::ClosePopupsExceptModals()\n{\n    ImGuiContext& g = *GImGui;\n\n    int popup_count_to_keep;\n    for (popup_count_to_keep = g.OpenPopupStack.Size; popup_count_to_keep > 0; popup_count_to_keep--)\n    {\n        ImGuiWindow* window = g.OpenPopupStack[popup_count_to_keep - 1].Window;\n        if (!window || (window->Flags & ImGuiWindowFlags_Modal))\n            break;\n    }\n    if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below\n        ClosePopupToLevel(popup_count_to_keep, true);\n}\n\nvoid ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup)\n{\n    ImGuiContext& g = *GImGui;\n    IMGUI_DEBUG_LOG_POPUP(\"[popup] ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\\n\", remaining, restore_focus_to_window_under_popup);\n    IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size);\n\n    // Trim open popup stack\n    ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window;\n    ImGuiWindow* popup_backup_nav_window = g.OpenPopupStack[remaining].BackupNavWindow;\n    g.OpenPopupStack.resize(remaining);\n\n    if (restore_focus_to_window_under_popup)\n    {\n        ImGuiWindow* focus_window = (popup_window && popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : popup_backup_nav_window;\n        if (focus_window && !focus_window->WasActive && popup_window)\n            FocusTopMostWindowUnderOne(popup_window, NULL, NULL, ImGuiFocusRequestFlags_RestoreFocusedChild); // Fallback\n        else\n            FocusWindow(focus_window, (g.NavLayer == ImGuiNavLayer_Main) ? ImGuiFocusRequestFlags_RestoreFocusedChild : ImGuiFocusRequestFlags_None);\n    }\n}\n\n// Close the popup we have begin-ed into.\nvoid ImGui::CloseCurrentPopup()\n{\n    ImGuiContext& g = *GImGui;\n    int popup_idx = g.BeginPopupStack.Size - 1;\n    if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.BeginPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId)\n        return;\n\n    // Closing a menu closes its top-most parent popup (unless a modal)\n    while (popup_idx > 0)\n    {\n        ImGuiWindow* popup_window = g.OpenPopupStack[popup_idx].Window;\n        ImGuiWindow* parent_popup_window = g.OpenPopupStack[popup_idx - 1].Window;\n        bool close_parent = false;\n        if (popup_window && (popup_window->Flags & ImGuiWindowFlags_ChildMenu))\n            if (parent_popup_window && !(parent_popup_window->Flags & ImGuiWindowFlags_MenuBar))\n                close_parent = true;\n        if (!close_parent)\n            break;\n        popup_idx--;\n    }\n    IMGUI_DEBUG_LOG_POPUP(\"[popup] CloseCurrentPopup %d -> %d\\n\", g.BeginPopupStack.Size - 1, popup_idx);\n    ClosePopupToLevel(popup_idx, true);\n\n    // A common pattern is to close a popup when selecting a menu item/selectable that will open another window.\n    // To improve this usage pattern, we avoid nav highlight for a single frame in the parent window.\n    // Similarly, we could avoid mouse hover highlight in this window but it is less visually problematic.\n    if (ImGuiWindow* window = g.NavWindow)\n        window->DC.NavHideHighlightOneFrame = true;\n}\n\n// Attention! BeginPopup() adds default flags which BeginPopupEx()!\nbool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (!IsPopupOpen(id, ImGuiPopupFlags_None))\n    {\n        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n        return false;\n    }\n\n    char name[20];\n    if (flags & ImGuiWindowFlags_ChildMenu)\n        ImFormatString(name, IM_ARRAYSIZE(name), \"##Menu_%02d\", g.BeginMenuCount); // Recycle windows based on depth\n    else\n        ImFormatString(name, IM_ARRAYSIZE(name), \"##Popup_%08x\", id); // Not recycling, so we can close/open during the same frame\n\n    flags |= ImGuiWindowFlags_Popup;\n    bool is_open = Begin(name, NULL, flags);\n    if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display)\n        EndPopup();\n\n    return is_open;\n}\n\nbool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.OpenPopupStack.Size <= g.BeginPopupStack.Size) // Early out for performance\n    {\n        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n        return false;\n    }\n    flags |= ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings;\n    ImGuiID id = g.CurrentWindow->GetID(str_id);\n    return BeginPopupEx(id, flags);\n}\n\n// If 'p_open' is specified for a modal popup window, the popup will have a regular close button which will close the popup.\n// Note that popup visibility status is owned by Dear ImGui (and manipulated with e.g. OpenPopup).\n// - *p_open set back to false in BeginPopupModal() when popup is not open.\n// - if you set *p_open to false before calling BeginPopupModal(), it will close the popup.\nbool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    const ImGuiID id = window->GetID(name);\n    if (!IsPopupOpen(id, ImGuiPopupFlags_None))\n    {\n        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n        if (p_open && *p_open)\n            *p_open = false;\n        return false;\n    }\n\n    // Center modal windows by default for increased visibility\n    // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves)\n    // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window.\n    if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0)\n    {\n        const ImGuiViewport* viewport = GetMainViewport();\n        SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));\n    }\n\n    flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse;\n    const bool is_open = Begin(name, p_open, flags);\n    if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display)\n    {\n        EndPopup();\n        if (is_open)\n            ClosePopupToLevel(g.BeginPopupStack.Size, true);\n        return false;\n    }\n    return is_open;\n}\n\nvoid ImGui::EndPopup()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginPopup()/EndPopup() calls\n    IM_ASSERT(g.BeginPopupStack.Size > 0);\n\n    // Make all menus and popups wrap around for now, may need to expose that policy (e.g. focus scope could include wrap/loop policy flags used by new move requests)\n    if (g.NavWindow == window)\n        NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY);\n\n    // Child-popups don't need to be laid out\n    IM_ASSERT(g.WithinEndChild == false);\n    if (window->Flags & ImGuiWindowFlags_ChildWindow)\n        g.WithinEndChild = true;\n    End();\n    g.WithinEndChild = false;\n}\n\n// Helper to open a popup if mouse button is released over the item\n// - This is essentially the same as BeginPopupContextItem() but without the trailing BeginPopup()\nvoid ImGui::OpenPopupOnItemClick(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);\n    if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))\n    {\n        ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!\n        IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)\n        OpenPopupEx(id, popup_flags);\n    }\n}\n\n// This is a helper to handle the simplest case of associating one named popup to one given widget.\n// - To create a popup associated to the last item, you generally want to pass a NULL value to str_id.\n// - To create a popup with a specific identifier, pass it in str_id.\n//    - This is useful when using using BeginPopupContextItem() on an item which doesn't have an identifier, e.g. a Text() call.\n//    - This is useful when multiple code locations may want to manipulate/open the same popup, given an explicit id.\n// - You may want to handle the whole on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters).\n//   This is essentially the same as:\n//       id = str_id ? GetID(str_id) : GetItemID();\n//       OpenPopupOnItemClick(str_id, ImGuiPopupFlags_MouseButtonRight);\n//       return BeginPopup(id);\n//   Which is essentially the same as:\n//       id = str_id ? GetID(str_id) : GetItemID();\n//       if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))\n//           OpenPopup(id);\n//       return BeginPopup(id);\n//   The main difference being that this is tweaked to avoid computing the ID twice.\nbool ImGui::BeginPopupContextItem(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n    ImGuiID id = str_id ? window->GetID(str_id) : g.LastItemData.ID;    // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict!\n    IM_ASSERT(id != 0);                                             // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item)\n    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);\n    if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))\n        OpenPopupEx(id, popup_flags);\n    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);\n}\n\nbool ImGui::BeginPopupContextWindow(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (!str_id)\n        str_id = \"window_context\";\n    ImGuiID id = window->GetID(str_id);\n    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);\n    if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup))\n        if (!(popup_flags & ImGuiPopupFlags_NoOpenOverItems) || !IsAnyItemHovered())\n            OpenPopupEx(id, popup_flags);\n    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);\n}\n\nbool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (!str_id)\n        str_id = \"void_context\";\n    ImGuiID id = window->GetID(str_id);\n    int mouse_button = (popup_flags & ImGuiPopupFlags_MouseButtonMask_);\n    if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow))\n        if (GetTopMostPopupModal() == NULL)\n            OpenPopupEx(id, popup_flags);\n    return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings);\n}\n\n// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.)\n// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it.\n// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor\n//  information are available, it may represent the entire platform monitor from the frame of reference of the current viewport.\n//  this allows us to have tooltips/popups displayed out of the parent viewport.)\nImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy)\n{\n    ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size);\n    //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255));\n    //GetForegroundDrawList()->AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255));\n\n    // Combo Box policy (we want a connecting edge)\n    if (policy == ImGuiPopupPositionPolicy_ComboBox)\n    {\n        const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up };\n        for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)\n        {\n            const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];\n            if (n != -1 && dir == *last_dir) // Already tried this direction?\n                continue;\n            ImVec2 pos;\n            if (dir == ImGuiDir_Down)  pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y);          // Below, Toward Right (default)\n            if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right\n            if (dir == ImGuiDir_Left)  pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left\n            if (dir == ImGuiDir_Up)    pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left\n            if (!r_outer.Contains(ImRect(pos, pos + size)))\n                continue;\n            *last_dir = dir;\n            return pos;\n        }\n    }\n\n    // Tooltip and Default popup policy\n    // (Always first try the direction we used on the last frame, if any)\n    if (policy == ImGuiPopupPositionPolicy_Tooltip || policy == ImGuiPopupPositionPolicy_Default)\n    {\n        const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left };\n        for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++)\n        {\n            const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n];\n            if (n != -1 && dir == *last_dir) // Already tried this direction?\n                continue;\n\n            const float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x);\n            const float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y);\n\n            // If there's not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width)\n            if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right))\n                continue;\n            if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down))\n                continue;\n\n            ImVec2 pos;\n            pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x;\n            pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y;\n\n            // Clamp top-left corner of popup\n            pos.x = ImMax(pos.x, r_outer.Min.x);\n            pos.y = ImMax(pos.y, r_outer.Min.y);\n\n            *last_dir = dir;\n            return pos;\n        }\n    }\n\n    // Fallback when not enough room:\n    *last_dir = ImGuiDir_None;\n\n    // For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.\n    if (policy == ImGuiPopupPositionPolicy_Tooltip)\n        return ref_pos + ImVec2(2, 2);\n\n    // Otherwise try to keep within display\n    ImVec2 pos = ref_pos;\n    pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x);\n    pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y);\n    return pos;\n}\n\n// Note that this is used for popups, which can overlap the non work-area of individual viewports.\nImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    IM_UNUSED(window);\n    ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect();\n    ImVec2 padding = g.Style.DisplaySafeAreaPadding;\n    r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f));\n    return r_screen;\n}\n\nImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n\n    ImRect r_outer = GetPopupAllowedExtentRect(window);\n    if (window->Flags & ImGuiWindowFlags_ChildMenu)\n    {\n        // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds.\n        // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu.\n        IM_ASSERT(g.CurrentWindow == window);\n        ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window;\n        float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x).\n        ImRect r_avoid;\n        if (parent_window->DC.MenuBarAppending)\n            r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field\n        else\n            r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX);\n        return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Default);\n    }\n    if (window->Flags & ImGuiWindowFlags_Popup)\n    {\n        return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, ImRect(window->Pos, window->Pos), ImGuiPopupPositionPolicy_Default); // Ideally we'd disable r_avoid here\n    }\n    if (window->Flags & ImGuiWindowFlags_Tooltip)\n    {\n        // Position tooltip (always follows mouse + clamp within outer boundaries)\n        // Note that drag and drop tooltips are NOT using this path: BeginTooltipEx() manually sets their position.\n        // In theory we could handle both cases in same location, but requires a bit of shuffling as drag and drop tooltips are calling SetWindowPos() leading to 'window_pos_set_by_api' being set in Begin()\n        IM_ASSERT(g.CurrentWindow == window);\n        const float scale = g.Style.MouseCursorScale;\n        const ImVec2 ref_pos = NavCalcPreferredRefPos();\n        const ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET * scale;\n        ImRect r_avoid;\n        if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))\n            r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);\n        else\n            r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.\n        //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));\n        return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);\n    }\n    IM_ASSERT(0);\n    return window->Pos;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] KEYBOARD/GAMEPAD NAVIGATION\n//-----------------------------------------------------------------------------\n\n// FIXME-NAV: The existence of SetNavID vs SetFocusID vs FocusWindow() needs to be clarified/reworked.\n// In our terminology those should be interchangeable, yet right now this is super confusing.\n// Those two functions are merely a legacy artifact, so at minimum naming should be clarified.\n\nvoid ImGui::SetNavWindow(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.NavWindow != window)\n    {\n        IMGUI_DEBUG_LOG_FOCUS(\"[focus] SetNavWindow(\\\"%s\\\")\\n\", window ? window->Name : \"<NULL>\");\n        g.NavWindow = window;\n        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;\n    }\n    g.NavInitRequest = g.NavMoveSubmitted = g.NavMoveScoringItems = false;\n    NavUpdateAnyRequestFlag();\n}\n\nvoid ImGui::NavClearPreferredPosForAxis(ImGuiAxis axis)\n{\n    ImGuiContext& g = *GImGui;\n    g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer][axis] = FLT_MAX;\n}\n\nvoid ImGui::SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.NavWindow != NULL);\n    IM_ASSERT(nav_layer == ImGuiNavLayer_Main || nav_layer == ImGuiNavLayer_Menu);\n    g.NavId = id;\n    g.NavLayer = nav_layer;\n    g.NavFocusScopeId = focus_scope_id;\n    g.NavWindow->NavLastIds[nav_layer] = id;\n    g.NavWindow->NavRectRel[nav_layer] = rect_rel;\n\n    // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)\n    NavClearPreferredPosForAxis(ImGuiAxis_X);\n    NavClearPreferredPosForAxis(ImGuiAxis_Y);\n}\n\nvoid ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(id != 0);\n\n    if (g.NavWindow != window)\n       SetNavWindow(window);\n\n    // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid.\n    // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text)\n    const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent;\n    g.NavId = id;\n    g.NavLayer = nav_layer;\n    g.NavFocusScopeId = g.CurrentFocusScopeId;\n    window->NavLastIds[nav_layer] = id;\n    if (g.LastItemData.ID == id)\n        window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect);\n\n    if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)\n        g.NavDisableMouseHover = true;\n    else\n        g.NavDisableHighlight = true;\n\n    // Clear preferred scoring position (NavMoveRequestApplyResult() will tend to restore it)\n    NavClearPreferredPosForAxis(ImGuiAxis_X);\n    NavClearPreferredPosForAxis(ImGuiAxis_Y);\n}\n\nstatic ImGuiDir ImGetDirQuadrantFromDelta(float dx, float dy)\n{\n    if (ImFabs(dx) > ImFabs(dy))\n        return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left;\n    return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up;\n}\n\nstatic float inline NavScoreItemDistInterval(float cand_min, float cand_max, float curr_min, float curr_max)\n{\n    if (cand_max < curr_min)\n        return cand_max - curr_min;\n    if (curr_max < cand_min)\n        return cand_min - curr_max;\n    return 0.0f;\n}\n\n// Scoring function for gamepad/keyboard directional navigation. Based on https://gist.github.com/rygorous/6981057\nstatic bool ImGui::NavScoreItem(ImGuiNavItemData* result)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (g.NavLayer != window->DC.NavLayerCurrent)\n        return false;\n\n    // FIXME: Those are not good variables names\n    ImRect cand = g.LastItemData.NavRect;   // Current item nav rectangle\n    const ImRect curr = g.NavScoringRect;   // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width)\n    g.NavScoringDebugCount++;\n\n    // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring\n    if (window->ParentWindow == g.NavWindow)\n    {\n        IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened);\n        if (!window->ClipRect.Overlaps(cand))\n            return false;\n        cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window\n    }\n\n    // Compute distance between boxes\n    // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed.\n    float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x);\n    float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items\n    if (dby != 0.0f && dbx != 0.0f)\n        dbx = (dbx / 1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f);\n    float dist_box = ImFabs(dbx) + ImFabs(dby);\n\n    // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter)\n    float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x);\n    float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y);\n    float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee)\n\n    // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance\n    ImGuiDir quadrant;\n    float dax = 0.0f, day = 0.0f, dist_axial = 0.0f;\n    if (dbx != 0.0f || dby != 0.0f)\n    {\n        // For non-overlapping boxes, use distance between boxes\n        dax = dbx;\n        day = dby;\n        dist_axial = dist_box;\n        quadrant = ImGetDirQuadrantFromDelta(dbx, dby);\n    }\n    else if (dcx != 0.0f || dcy != 0.0f)\n    {\n        // For overlapping boxes with different centers, use distance between centers\n        dax = dcx;\n        day = dcy;\n        dist_axial = dist_center;\n        quadrant = ImGetDirQuadrantFromDelta(dcx, dcy);\n    }\n    else\n    {\n        // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter)\n        quadrant = (g.LastItemData.ID < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right;\n    }\n\n    const ImGuiDir move_dir = g.NavMoveDir;\n#if IMGUI_DEBUG_NAV_SCORING\n    char buf[200];\n    if (g.IO.KeyCtrl) // Hold CTRL to preview score in matching quadrant. CTRL+Arrow to rotate.\n    {\n        if (quadrant == move_dir)\n        {\n            ImFormatString(buf, IM_ARRAYSIZE(buf), \"%.0f/%.0f\", dist_box, dist_center);\n            ImDrawList* draw_list = GetForegroundDrawList(window);\n            draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 80));\n            draw_list->AddRectFilled(cand.Min, cand.Min + CalcTextSize(buf), IM_COL32(255, 0, 0, 200));\n            draw_list->AddText(cand.Min, IM_COL32(255, 255, 255, 255), buf);\n        }\n    }\n    const bool debug_hovering = IsMouseHoveringRect(cand.Min, cand.Max);\n    const bool debug_tty = (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_Space));\n    if (debug_hovering || debug_tty)\n    {\n        ImFormatString(buf, IM_ARRAYSIZE(buf),\n            \"d-box    (%7.3f,%7.3f) -> %7.3f\\nd-center (%7.3f,%7.3f) -> %7.3f\\nd-axial  (%7.3f,%7.3f) -> %7.3f\\nnav %c, quadrant %c\",\n            dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, \"-WENS\"[move_dir+1], \"-WENS\"[quadrant+1]);\n        if (debug_hovering)\n        {\n            ImDrawList* draw_list = GetForegroundDrawList(window);\n            draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255, 200, 0, 100));\n            draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255, 255, 0, 200));\n            draw_list->AddRectFilled(cand.Max - ImVec2(4, 4), cand.Max + CalcTextSize(buf) + ImVec2(4, 4), IM_COL32(40, 0, 0, 200));\n            draw_list->AddText(cand.Max, ~0U, buf);\n        }\n        if (debug_tty) { IMGUI_DEBUG_LOG_NAV(\"id 0x%08X\\n%s\\n\", g.LastItemData.ID, buf); }\n    }\n#endif\n\n    // Is it in the quadrant we're interested in moving to?\n    bool new_best = false;\n    if (quadrant == move_dir)\n    {\n        // Does it beat the current best candidate?\n        if (dist_box < result->DistBox)\n        {\n            result->DistBox = dist_box;\n            result->DistCenter = dist_center;\n            return true;\n        }\n        if (dist_box == result->DistBox)\n        {\n            // Try using distance between center points to break ties\n            if (dist_center < result->DistCenter)\n            {\n                result->DistCenter = dist_center;\n                new_best = true;\n            }\n            else if (dist_center == result->DistCenter)\n            {\n                // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving \"later\" items\n                // (with higher index) to the right/downwards by an infinitesimal amount since we the current \"best\" button already (so it must have a lower index),\n                // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis.\n                if (((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance\n                    new_best = true;\n            }\n        }\n    }\n\n    // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no \"real\" matches\n    // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness)\n    // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too.\n    // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward.\n    // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option?\n    if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial)  // Check axial match\n        if (g.NavLayer == ImGuiNavLayer_Menu && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))\n            if ((move_dir == ImGuiDir_Left && dax < 0.0f) || (move_dir == ImGuiDir_Right && dax > 0.0f) || (move_dir == ImGuiDir_Up && day < 0.0f) || (move_dir == ImGuiDir_Down && day > 0.0f))\n            {\n                result->DistAxial = dist_axial;\n                new_best = true;\n            }\n\n    return new_best;\n}\n\nstatic void ImGui::NavApplyItemToResult(ImGuiNavItemData* result)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    result->Window = window;\n    result->ID = g.LastItemData.ID;\n    result->FocusScopeId = g.CurrentFocusScopeId;\n    result->InFlags = g.LastItemData.InFlags;\n    result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect);\n    if (result->InFlags & ImGuiItemFlags_HasSelectionUserData)\n    {\n        IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);\n        result->SelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.\n    }\n}\n\n// True when current work location may be scrolled horizontally when moving left / right.\n// This is generally always true UNLESS within a column. We don't have a vertical equivalent.\nvoid ImGui::NavUpdateCurrentWindowIsScrollPushableX()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    window->DC.NavIsScrollPushableX = (g.CurrentTable == NULL && window->DC.CurrentColumns == NULL);\n}\n\n// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above)\n// This is called after LastItemData is set, but NextItemData is also still valid.\nstatic void ImGui::NavProcessItem()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    const ImGuiID id = g.LastItemData.ID;\n    const ImGuiItemFlags item_flags = g.LastItemData.InFlags;\n\n    // When inside a container that isn't scrollable with Left<>Right, clip NavRect accordingly (#2221)\n    if (window->DC.NavIsScrollPushableX == false)\n    {\n        g.LastItemData.NavRect.Min.x = ImClamp(g.LastItemData.NavRect.Min.x, window->ClipRect.Min.x, window->ClipRect.Max.x);\n        g.LastItemData.NavRect.Max.x = ImClamp(g.LastItemData.NavRect.Max.x, window->ClipRect.Min.x, window->ClipRect.Max.x);\n    }\n    const ImRect nav_bb = g.LastItemData.NavRect;\n\n    // Process Init Request\n    if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent && (item_flags & ImGuiItemFlags_Disabled) == 0)\n    {\n        // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback\n        const bool candidate_for_nav_default_focus = (item_flags & ImGuiItemFlags_NoNavDefaultFocus) == 0;\n        if (candidate_for_nav_default_focus || g.NavInitResult.ID == 0)\n        {\n            NavApplyItemToResult(&g.NavInitResult);\n        }\n        if (candidate_for_nav_default_focus)\n        {\n            g.NavInitRequest = false; // Found a match, clear request\n            NavUpdateAnyRequestFlag();\n        }\n    }\n\n    // Process Move Request (scoring for navigation)\n    // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRect + scoring from a rect wrapped according to current wrapping policy)\n    if (g.NavMoveScoringItems && (item_flags & ImGuiItemFlags_Disabled) == 0)\n    {\n        const bool is_tabbing = (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) != 0;\n        if (is_tabbing)\n        {\n            NavProcessItemForTabbingRequest(id, item_flags, g.NavMoveFlags);\n        }\n        else if (g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId))\n        {\n            ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther;\n            if (NavScoreItem(result))\n                NavApplyItemToResult(result);\n\n            // Features like PageUp/PageDown need to maintain a separate score for the visible set of items.\n            const float VISIBLE_RATIO = 0.70f;\n            if ((g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb))\n                if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO)\n                    if (NavScoreItem(&g.NavMoveResultLocalVisible))\n                        NavApplyItemToResult(&g.NavMoveResultLocalVisible);\n        }\n    }\n\n    // Update information for currently focused/navigated item\n    if (g.NavId == id)\n    {\n        if (g.NavWindow != window)\n            SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window.\n        g.NavLayer = window->DC.NavLayerCurrent;\n        g.NavFocusScopeId = g.CurrentFocusScopeId;\n        g.NavIdIsAlive = true;\n        if (g.LastItemData.InFlags & ImGuiItemFlags_HasSelectionUserData)\n        {\n            IM_ASSERT(g.NextItemData.SelectionUserData != ImGuiSelectionUserData_Invalid);\n            g.NavLastValidSelectionUserData = g.NextItemData.SelectionUserData; // INTENTIONAL: At this point this field is not cleared in NextItemData. Avoid unnecessary copy to LastItemData.\n        }\n        window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position)\n    }\n}\n\n// Handle \"scoring\" of an item for a tabbing/focusing request initiated by NavUpdateCreateTabbingRequest().\n// Note that SetKeyboardFocusHere() API calls are considered tabbing requests!\n// - Case 1: no nav/active id:    set result to first eligible item, stop storing.\n// - Case 2: tab forward:         on ref id set counter, on counter elapse store result\n// - Case 3: tab forward wrap:    set result to first eligible item (preemptively), on ref id set counter, on next frame if counter hasn't elapsed store result. // FIXME-TABBING: Could be done as a next-frame forwarded request\n// - Case 4: tab backward:        store all results, on ref id pick prev, stop storing\n// - Case 5: tab backward wrap:   store all results, on ref id if no result keep storing until last // FIXME-TABBING: Could be done as next-frame forwarded requested\nvoid ImGui::NavProcessItemForTabbingRequest(ImGuiID id, ImGuiItemFlags item_flags, ImGuiNavMoveFlags move_flags)\n{\n    ImGuiContext& g = *GImGui;\n\n    if ((move_flags & ImGuiNavMoveFlags_FocusApi) == 0)\n        if (g.NavLayer != g.CurrentWindow->DC.NavLayerCurrent)\n            return;\n\n    // - Can always land on an item when using API call.\n    // - Tabbing with _NavEnableKeyboard (space/enter/arrows): goes through every item.\n    // - Tabbing without _NavEnableKeyboard: goes through inputable items only.\n    bool can_stop;\n    if (move_flags & ImGuiNavMoveFlags_FocusApi)\n        can_stop = true;\n    else\n        can_stop = (item_flags & ImGuiItemFlags_NoTabStop) == 0 && ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) || (item_flags & ImGuiItemFlags_Inputable));\n\n    // Always store in NavMoveResultLocal (unlike directional request which uses NavMoveResultOther on sibling/flattened windows)\n    ImGuiNavItemData* result = &g.NavMoveResultLocal;\n    if (g.NavTabbingDir == +1)\n    {\n        // Tab Forward or SetKeyboardFocusHere() with >= 0\n        if (can_stop && g.NavTabbingResultFirst.ID == 0)\n            NavApplyItemToResult(&g.NavTabbingResultFirst);\n        if (can_stop && g.NavTabbingCounter > 0 && --g.NavTabbingCounter == 0)\n            NavMoveRequestResolveWithLastItem(result);\n        else if (g.NavId == id)\n            g.NavTabbingCounter = 1;\n    }\n    else if (g.NavTabbingDir == -1)\n    {\n        // Tab Backward\n        if (g.NavId == id)\n        {\n            if (result->ID)\n            {\n                g.NavMoveScoringItems = false;\n                NavUpdateAnyRequestFlag();\n            }\n        }\n        else if (can_stop)\n        {\n            // Keep applying until reaching NavId\n            NavApplyItemToResult(result);\n        }\n    }\n    else if (g.NavTabbingDir == 0)\n    {\n        if (can_stop && g.NavId == id)\n            NavMoveRequestResolveWithLastItem(result);\n        if (can_stop && g.NavTabbingResultFirst.ID == 0) // Tab init\n            NavApplyItemToResult(&g.NavTabbingResultFirst);\n    }\n}\n\nbool ImGui::NavMoveRequestButNoResultYet()\n{\n    ImGuiContext& g = *GImGui;\n    return g.NavMoveScoringItems && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0;\n}\n\n// FIXME: ScoringRect is not set\nvoid ImGui::NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.NavWindow != NULL);\n\n    if (move_flags & ImGuiNavMoveFlags_IsTabbing)\n        move_flags |= ImGuiNavMoveFlags_AllowCurrentNavId;\n\n    g.NavMoveSubmitted = g.NavMoveScoringItems = true;\n    g.NavMoveDir = move_dir;\n    g.NavMoveDirForDebug = move_dir;\n    g.NavMoveClipDir = clip_dir;\n    g.NavMoveFlags = move_flags;\n    g.NavMoveScrollFlags = scroll_flags;\n    g.NavMoveForwardToNextFrame = false;\n    g.NavMoveKeyMods = (move_flags & ImGuiNavMoveFlags_FocusApi) ? 0 : g.IO.KeyMods;\n    g.NavMoveResultLocal.Clear();\n    g.NavMoveResultLocalVisible.Clear();\n    g.NavMoveResultOther.Clear();\n    g.NavTabbingCounter = 0;\n    g.NavTabbingResultFirst.Clear();\n    NavUpdateAnyRequestFlag();\n}\n\nvoid ImGui::NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result)\n{\n    ImGuiContext& g = *GImGui;\n    g.NavMoveScoringItems = false; // Ensure request doesn't need more processing\n    NavApplyItemToResult(result);\n    NavUpdateAnyRequestFlag();\n}\n\n// Called by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere\nvoid ImGui::NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiNavTreeNodeData* tree_node_data)\n{\n    ImGuiContext& g = *GImGui;\n    g.NavMoveScoringItems = false;\n    g.LastItemData.ID = tree_node_data->ID;\n    g.LastItemData.InFlags = tree_node_data->InFlags & ~ImGuiItemFlags_HasSelectionUserData; // Losing SelectionUserData, recovered next-frame (cheaper).\n    g.LastItemData.NavRect = tree_node_data->NavRect;\n    NavApplyItemToResult(result); // Result this instead of implementing a NavApplyPastTreeNodeToResult()\n    NavClearPreferredPosForAxis(ImGuiAxis_Y);\n    NavUpdateAnyRequestFlag();\n}\n\nvoid ImGui::NavMoveRequestCancel()\n{\n    ImGuiContext& g = *GImGui;\n    g.NavMoveSubmitted = g.NavMoveScoringItems = false;\n    NavUpdateAnyRequestFlag();\n}\n\n// Forward will reuse the move request again on the next frame (generally with modifications done to it)\nvoid ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.NavMoveForwardToNextFrame == false);\n    NavMoveRequestCancel();\n    g.NavMoveForwardToNextFrame = true;\n    g.NavMoveDir = move_dir;\n    g.NavMoveClipDir = clip_dir;\n    g.NavMoveFlags = move_flags | ImGuiNavMoveFlags_Forwarded;\n    g.NavMoveScrollFlags = scroll_flags;\n}\n\n// Navigation wrap-around logic is delayed to the end of the frame because this operation is only valid after entire\n// popup is assembled and in case of appended popups it is not clear which EndPopup() call is final.\nvoid ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags wrap_flags)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT((wrap_flags & ImGuiNavMoveFlags_WrapMask_ ) != 0 && (wrap_flags & ~ImGuiNavMoveFlags_WrapMask_) == 0); // Call with _WrapX, _WrapY, _LoopX, _LoopY\n\n    // In theory we should test for NavMoveRequestButNoResultYet() but there's no point doing it:\n    // as NavEndFrame() will do the same test. It will end up calling NavUpdateCreateWrappingRequest().\n    if (g.NavWindow == window && g.NavMoveScoringItems && g.NavLayer == ImGuiNavLayer_Main)\n        g.NavMoveFlags = (g.NavMoveFlags & ~ImGuiNavMoveFlags_WrapMask_) | wrap_flags;\n}\n\n// FIXME: This could be replaced by updating a frame number in each window when (window == NavWindow) and (NavLayer == 0).\n// This way we could find the last focused window among our children. It would be much less confusing this way?\nstatic void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window)\n{\n    ImGuiWindow* parent = nav_window;\n    while (parent && parent->RootWindow != parent && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)\n        parent = parent->ParentWindow;\n    if (parent && parent != nav_window)\n        parent->NavLastChildNavWindow = nav_window;\n}\n\n// Restore the last focused child.\n// Call when we are expected to land on the Main Layer (0) after FocusWindow()\nstatic ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window)\n{\n    if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive)\n        return window->NavLastChildNavWindow;\n    return window;\n}\n\nvoid ImGui::NavRestoreLayer(ImGuiNavLayer layer)\n{\n    ImGuiContext& g = *GImGui;\n    if (layer == ImGuiNavLayer_Main)\n    {\n        ImGuiWindow* prev_nav_window = g.NavWindow;\n        g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow);    // FIXME-NAV: Should clear ongoing nav requests?\n        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;\n        if (prev_nav_window)\n            IMGUI_DEBUG_LOG_FOCUS(\"[focus] NavRestoreLayer: from \\\"%s\\\" to SetNavWindow(\\\"%s\\\")\\n\", prev_nav_window->Name, g.NavWindow->Name);\n    }\n    ImGuiWindow* window = g.NavWindow;\n    if (window->NavLastIds[layer] != 0)\n    {\n        SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);\n    }\n    else\n    {\n        g.NavLayer = layer;\n        NavInitWindow(window, true);\n    }\n}\n\nvoid ImGui::NavRestoreHighlightAfterMove()\n{\n    ImGuiContext& g = *GImGui;\n    g.NavDisableHighlight = false;\n    g.NavDisableMouseHover = g.NavMousePosDirty = true;\n}\n\nstatic inline void ImGui::NavUpdateAnyRequestFlag()\n{\n    ImGuiContext& g = *GImGui;\n    g.NavAnyRequest = g.NavMoveScoringItems || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL);\n    if (g.NavAnyRequest)\n        IM_ASSERT(g.NavWindow != NULL);\n}\n\n// This needs to be called before we submit any widget (aka in or before Begin)\nvoid ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(window == g.NavWindow);\n\n    if (window->Flags & ImGuiWindowFlags_NoNavInputs)\n    {\n        g.NavId = 0;\n        g.NavFocusScopeId = window->NavRootFocusScopeId;\n        return;\n    }\n\n    bool init_for_nav = false;\n    if (window == window->RootWindow || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit)\n        init_for_nav = true;\n    IMGUI_DEBUG_LOG_NAV(\"[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\\\"%s\\\", layer=%d\\n\", init_for_nav, window->Name, g.NavLayer);\n    if (init_for_nav)\n    {\n        SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect());\n        g.NavInitRequest = true;\n        g.NavInitRequestFromMove = false;\n        g.NavInitResult.ID = 0;\n        NavUpdateAnyRequestFlag();\n    }\n    else\n    {\n        g.NavId = window->NavLastIds[0];\n        g.NavFocusScopeId = window->NavRootFocusScopeId;\n    }\n}\n\nstatic ImVec2 ImGui::NavCalcPreferredRefPos()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.NavWindow;\n    if (g.NavDisableHighlight || !g.NavDisableMouseHover || !window)\n    {\n        // Mouse (we need a fallback in case the mouse becomes invalid after being used)\n        // The +1.0f offset when stored by OpenPopupEx() allows reopening this or another popup (same or another mouse button) while not moving the mouse, it is pretty standard.\n        // In theory we could move that +1.0f offset in OpenPopupEx()\n        ImVec2 p = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : g.MouseLastValidPos;\n        return ImVec2(p.x + 1.0f, p.y);\n    }\n    else\n    {\n        // When navigation is active and mouse is disabled, pick a position around the bottom left of the currently navigated item\n        // Take account of upcoming scrolling (maybe set mouse pos should be done in EndFrame?)\n        ImRect rect_rel = WindowRectRelToAbs(window, window->NavRectRel[g.NavLayer]);\n        if (window->LastFrameActive != g.FrameCount && (window->ScrollTarget.x != FLT_MAX || window->ScrollTarget.y != FLT_MAX))\n        {\n            ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window);\n            rect_rel.Translate(window->Scroll - next_scroll);\n        }\n        ImVec2 pos = ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight()));\n        ImGuiViewport* viewport = GetMainViewport();\n        return ImTrunc(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta.\n    }\n}\n\nfloat ImGui::GetNavTweakPressedAmount(ImGuiAxis axis)\n{\n    ImGuiContext& g = *GImGui;\n    float repeat_delay, repeat_rate;\n    GetTypematicRepeatRate(ImGuiInputFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate);\n\n    ImGuiKey key_less, key_more;\n    if (g.NavInputSource == ImGuiInputSource_Gamepad)\n    {\n        key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp;\n        key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown;\n    }\n    else\n    {\n        key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow;\n        key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow;\n    }\n    float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate);\n    if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase\n        amount = 0.0f;\n    return amount;\n}\n\nstatic void ImGui::NavUpdate()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    io.WantSetMousePos = false;\n    //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG_NAV(\"[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\\n\", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : \"NULL\", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest);\n\n    // Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard)\n    // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource?\n    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;\n    const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown };\n    if (nav_gamepad_active)\n        for (ImGuiKey key : nav_gamepad_keys_to_change_source)\n            if (IsKeyDown(key))\n                g.NavInputSource = ImGuiInputSource_Gamepad;\n    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n    const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow };\n    if (nav_keyboard_active)\n        for (ImGuiKey key : nav_keyboard_keys_to_change_source)\n            if (IsKeyDown(key))\n                g.NavInputSource = ImGuiInputSource_Keyboard;\n\n    // Process navigation init request (select first/default focus)\n    g.NavJustMovedToId = 0;\n    if (g.NavInitResult.ID != 0)\n        NavInitRequestApplyResult();\n    g.NavInitRequest = false;\n    g.NavInitRequestFromMove = false;\n    g.NavInitResult.ID = 0;\n\n    // Process navigation move request\n    if (g.NavMoveSubmitted)\n        NavMoveRequestApplyResult();\n    g.NavTabbingCounter = 0;\n    g.NavMoveSubmitted = g.NavMoveScoringItems = false;\n\n    // Schedule mouse position update (will be done at the bottom of this function, after 1) processing all move requests and 2) updating scrolling)\n    bool set_mouse_pos = false;\n    if (g.NavMousePosDirty && g.NavIdIsAlive)\n        if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow)\n            set_mouse_pos = true;\n    g.NavMousePosDirty = false;\n    IM_ASSERT(g.NavLayer == ImGuiNavLayer_Main || g.NavLayer == ImGuiNavLayer_Menu);\n\n    // Store our return window (for returning from Menu Layer to Main Layer) and clear it as soon as we step back in our own Layer 0\n    if (g.NavWindow)\n        NavSaveLastChildNavWindowIntoParent(g.NavWindow);\n    if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == ImGuiNavLayer_Main)\n        g.NavWindow->NavLastChildNavWindow = NULL;\n\n    // Update CTRL+TAB and Windowing features (hold Square to move/resize/etc.)\n    NavUpdateWindowing();\n\n    // Set output flags for user application\n    io.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs);\n    io.NavVisible = (io.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL);\n\n    // Process NavCancel input (to close a popup, get back to parent, clear focus)\n    NavUpdateCancelRequest();\n\n    // Process manual activation request\n    g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = 0;\n    g.NavActivateFlags = ImGuiActivateFlags_None;\n    if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))\n    {\n        const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate));\n        const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, false)));\n        const bool input_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Enter)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput));\n        const bool input_pressed = input_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Enter, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, false)));\n        if (g.ActiveId == 0 && activate_pressed)\n        {\n            g.NavActivateId = g.NavId;\n            g.NavActivateFlags = ImGuiActivateFlags_PreferTweak;\n        }\n        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && input_pressed)\n        {\n            g.NavActivateId = g.NavId;\n            g.NavActivateFlags = ImGuiActivateFlags_PreferInput;\n        }\n        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_down || input_down))\n            g.NavActivateDownId = g.NavId;\n        if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && (activate_pressed || input_pressed))\n            g.NavActivatePressedId = g.NavId;\n    }\n    if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs))\n        g.NavDisableHighlight = true;\n    if (g.NavActivateId != 0)\n        IM_ASSERT(g.NavActivateDownId == g.NavActivateId);\n\n    // Process programmatic activation request\n    // FIXME-NAV: Those should eventually be queued (unlike focus they don't cancel each others)\n    if (g.NavNextActivateId != 0)\n    {\n        g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavNextActivateId;\n        g.NavActivateFlags = g.NavNextActivateFlags;\n    }\n    g.NavNextActivateId = 0;\n\n    // Process move requests\n    NavUpdateCreateMoveRequest();\n    if (g.NavMoveDir == ImGuiDir_None)\n        NavUpdateCreateTabbingRequest();\n    NavUpdateAnyRequestFlag();\n    g.NavIdIsAlive = false;\n\n    // Scrolling\n    if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget)\n    {\n        // *Fallback* manual-scroll with Nav directional keys when window has no navigable item\n        ImGuiWindow* window = g.NavWindow;\n        const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported.\n        const ImGuiDir move_dir = g.NavMoveDir;\n        if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None)\n        {\n            if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right)\n                SetScrollX(window, ImTrunc(window->Scroll.x + ((move_dir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed));\n            if (move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down)\n                SetScrollY(window, ImTrunc(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed));\n        }\n\n        // *Normal* Manual scroll with LStick\n        // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds.\n        if (nav_gamepad_active)\n        {\n            const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);\n            const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f;\n            if (scroll_dir.x != 0.0f && window->ScrollbarX)\n                SetScrollX(window, ImTrunc(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor));\n            if (scroll_dir.y != 0.0f)\n                SetScrollY(window, ImTrunc(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor));\n        }\n    }\n\n    // Always prioritize mouse highlight if navigation is disabled\n    if (!nav_keyboard_active && !nav_gamepad_active)\n    {\n        g.NavDisableHighlight = true;\n        g.NavDisableMouseHover = set_mouse_pos = false;\n    }\n\n    // Update mouse position if requested\n    // (This will take into account the possibility that a Scroll was queued in the window to offset our absolute mouse position before scroll has been applied)\n    if (set_mouse_pos && (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos))\n        TeleportMousePos(NavCalcPreferredRefPos());\n\n    // [DEBUG]\n    g.NavScoringDebugCount = 0;\n#if IMGUI_DEBUG_NAV_RECTS\n    if (ImGuiWindow* debug_window = g.NavWindow)\n    {\n        ImDrawList* draw_list = GetForegroundDrawList(debug_window);\n        int layer = g.NavLayer; /* for (int layer = 0; layer < 2; layer++)*/ { ImRect r = WindowRectRelToAbs(debug_window, debug_window->NavRectRel[layer]); draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 200, 0, 255)); }\n        //if (1) { ImU32 col = (!debug_window->Hidden) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, \"%d\", g.NavLayer); draw_list->AddCircleFilled(p, 3.0f, col); draw_list->AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); }\n    }\n#endif\n}\n\nvoid ImGui::NavInitRequestApplyResult()\n{\n    // In very rare cases g.NavWindow may be null (e.g. clearing focus after requesting an init request, which does happen when releasing Alt while clicking on void)\n    ImGuiContext& g = *GImGui;\n    if (!g.NavWindow)\n        return;\n\n    ImGuiNavItemData* result = &g.NavInitResult;\n    if (g.NavId != result->ID)\n    {\n        g.NavJustMovedToId = result->ID;\n        g.NavJustMovedToFocusScopeId = result->FocusScopeId;\n        g.NavJustMovedToKeyMods = 0;\n    }\n\n    // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called)\n    // FIXME-NAV: On _NavFlattened windows, g.NavWindow will only be updated during subsequent frame. Not a problem currently.\n    IMGUI_DEBUG_LOG_NAV(\"[nav] NavInitRequest: ApplyResult: NavID 0x%08X in Layer %d Window \\\"%s\\\"\\n\", result->ID, g.NavLayer, g.NavWindow->Name);\n    SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);\n    g.NavIdIsAlive = true; // Mark as alive from previous frame as we got a result\n    if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)\n        g.NavLastValidSelectionUserData = result->SelectionUserData;\n    if (g.NavInitRequestFromMove)\n        NavRestoreHighlightAfterMove();\n}\n\n// Bias scoring rect ahead of scoring + update preferred pos (if missing) using source position\nstatic void NavBiasScoringRect(ImRect& r, ImVec2& preferred_pos_rel, ImGuiDir move_dir, ImGuiNavMoveFlags move_flags)\n{\n    // Bias initial rect\n    ImGuiContext& g = *GImGui;\n    const ImVec2 rel_to_abs_offset = g.NavWindow->DC.CursorStartPos;\n\n    // Initialize bias on departure if we don't have any. So mouse-click + arrow will record bias.\n    // - We default to L/U bias, so moving down from a large source item into several columns will land on left-most column.\n    // - But each successful move sets new bias on one axis, only cleared when using mouse.\n    if ((move_flags & ImGuiNavMoveFlags_Forwarded) == 0)\n    {\n        if (preferred_pos_rel.x == FLT_MAX)\n            preferred_pos_rel.x = ImMin(r.Min.x + 1.0f, r.Max.x) - rel_to_abs_offset.x;\n        if (preferred_pos_rel.y == FLT_MAX)\n            preferred_pos_rel.y = r.GetCenter().y - rel_to_abs_offset.y;\n    }\n\n    // Apply general bias on the other axis\n    if ((move_dir == ImGuiDir_Up || move_dir == ImGuiDir_Down) && preferred_pos_rel.x != FLT_MAX)\n        r.Min.x = r.Max.x = preferred_pos_rel.x + rel_to_abs_offset.x;\n    else if ((move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) && preferred_pos_rel.y != FLT_MAX)\n        r.Min.y = r.Max.y = preferred_pos_rel.y + rel_to_abs_offset.y;\n}\n\nvoid ImGui::NavUpdateCreateMoveRequest()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n    ImGuiWindow* window = g.NavWindow;\n    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;\n    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n\n    if (g.NavMoveForwardToNextFrame && window != NULL)\n    {\n        // Forwarding previous request (which has been modified, e.g. wrap around menus rewrite the requests with a starting rectangle at the other side of the window)\n        // (preserve most state, which were already set by the NavMoveRequestForward() function)\n        IM_ASSERT(g.NavMoveDir != ImGuiDir_None && g.NavMoveClipDir != ImGuiDir_None);\n        IM_ASSERT(g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded);\n        IMGUI_DEBUG_LOG_NAV(\"[nav] NavMoveRequestForward %d\\n\", g.NavMoveDir);\n    }\n    else\n    {\n        // Initiate directional inputs request\n        g.NavMoveDir = ImGuiDir_None;\n        g.NavMoveFlags = ImGuiNavMoveFlags_None;\n        g.NavMoveScrollFlags = ImGuiScrollFlags_None;\n        if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs))\n        {\n            const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove;\n            if (!IsActiveIdUsingNavDir(ImGuiDir_Left)  && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft,  ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow,  ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Left; }\n            if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Right; }\n            if (!IsActiveIdUsingNavDir(ImGuiDir_Up)    && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp,    ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow,    ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Up; }\n            if (!IsActiveIdUsingNavDir(ImGuiDir_Down)  && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown,  ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow,  ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Down; }\n        }\n        g.NavMoveClipDir = g.NavMoveDir;\n        g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);\n    }\n\n    // Update PageUp/PageDown/Home/End scroll\n    // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag?\n    float scoring_rect_offset_y = 0.0f;\n    if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)\n        scoring_rect_offset_y = NavUpdatePageUpPageDown();\n    if (scoring_rect_offset_y != 0.0f)\n    {\n        g.NavScoringNoClipRect = window->InnerRect;\n        g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y);\n    }\n\n    // [DEBUG] Always send a request when holding CTRL. Hold CTRL + Arrow change the direction.\n#if IMGUI_DEBUG_NAV_SCORING\n    //if (io.KeyCtrl && IsKeyPressed(ImGuiKey_C))\n    //    g.NavMoveDirForDebug = (ImGuiDir)((g.NavMoveDirForDebug + 1) & 3);\n    if (io.KeyCtrl)\n    {\n        if (g.NavMoveDir == ImGuiDir_None)\n            g.NavMoveDir = g.NavMoveDirForDebug;\n        g.NavMoveClipDir = g.NavMoveDir;\n        g.NavMoveFlags |= ImGuiNavMoveFlags_DebugNoResult;\n    }\n#endif\n\n    // Submit\n    g.NavMoveForwardToNextFrame = false;\n    if (g.NavMoveDir != ImGuiDir_None)\n        NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags);\n\n    // Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match)\n    if (g.NavMoveSubmitted && g.NavId == 0)\n    {\n        IMGUI_DEBUG_LOG_NAV(\"[nav] NavInitRequest: from move, window \\\"%s\\\", layer=%d\\n\", window ? window->Name : \"<NULL>\", g.NavLayer);\n        g.NavInitRequest = g.NavInitRequestFromMove = true;\n        g.NavInitResult.ID = 0;\n        g.NavDisableHighlight = false;\n    }\n\n    // When using gamepad, we project the reference nav bounding box into window visible area.\n    // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling,\n    // since with gamepad all movements are relative (can't focus a visible object like we can with the mouse).\n    if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded))\n    {\n        bool clamp_x = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_WrapX)) == 0;\n        bool clamp_y = (g.NavMoveFlags & (ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapY)) == 0;\n        ImRect inner_rect_rel = WindowRectAbsToRel(window, ImRect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)));\n\n        // Take account of changing scroll to handle triggering a new move request on a scrolling frame. (#6171)\n        // Otherwise 'inner_rect_rel' would be off on the move result frame.\n        inner_rect_rel.Translate(CalcNextScrollFromScrollTargetAndClamp(window) - window->Scroll);\n\n        if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer]))\n        {\n            IMGUI_DEBUG_LOG_NAV(\"[nav] NavMoveRequest: clamp NavRectRel for gamepad move\\n\");\n            float pad_x = ImMin(inner_rect_rel.GetWidth(), window->CalcFontSize() * 0.5f);\n            float pad_y = ImMin(inner_rect_rel.GetHeight(), window->CalcFontSize() * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item\n            inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX;\n            inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX;\n            inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX;\n            inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX;\n            window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel);\n            g.NavId = 0;\n        }\n    }\n\n    // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items)\n    ImRect scoring_rect;\n    if (window != NULL)\n    {\n        ImRect nav_rect_rel = !window->NavRectRel[g.NavLayer].IsInverted() ? window->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0);\n        scoring_rect = WindowRectRelToAbs(window, nav_rect_rel);\n        scoring_rect.TranslateY(scoring_rect_offset_y);\n        if (g.NavMoveSubmitted)\n            NavBiasScoringRect(scoring_rect, window->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer], g.NavMoveDir, g.NavMoveFlags);\n        IM_ASSERT(!scoring_rect.IsInverted()); // Ensure we have a non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem().\n        //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]\n        //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG]\n    }\n    g.NavScoringRect = scoring_rect;\n    g.NavScoringNoClipRect.Add(scoring_rect);\n}\n\nvoid ImGui::NavUpdateCreateTabbingRequest()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.NavWindow;\n    IM_ASSERT(g.NavMoveDir == ImGuiDir_None);\n    if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs))\n        return;\n\n    const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat) && !g.IO.KeyCtrl && !g.IO.KeyAlt;\n    if (!tab_pressed)\n        return;\n\n    // Initiate tabbing request\n    // (this is ALWAYS ENABLED, regardless of ImGuiConfigFlags_NavEnableKeyboard flag!)\n    // See NavProcessItemForTabbingRequest() for a description of the various forward/backward tabbing cases with and without wrapping.\n    const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n    if (nav_keyboard_active)\n        g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.NavDisableHighlight == true && g.ActiveId == 0) ? 0 : +1;\n    else\n        g.NavTabbingDir = g.IO.KeyShift ? -1 : (g.ActiveId == 0) ? 0 : +1;\n    ImGuiNavMoveFlags move_flags = ImGuiNavMoveFlags_IsTabbing | ImGuiNavMoveFlags_Activate;\n    ImGuiScrollFlags scroll_flags = window->Appearing ? ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleEdgeY;\n    ImGuiDir clip_dir = (g.NavTabbingDir < 0) ? ImGuiDir_Up : ImGuiDir_Down;\n    NavMoveRequestSubmit(ImGuiDir_None, clip_dir, move_flags, scroll_flags); // FIXME-NAV: Once we refactor tabbing, add LegacyApi flag to not activate non-inputable.\n    g.NavTabbingCounter = -1;\n}\n\n// Apply result from previous frame navigation directional move request. Always called from NavUpdate()\nvoid ImGui::NavMoveRequestApplyResult()\n{\n    ImGuiContext& g = *GImGui;\n#if IMGUI_DEBUG_NAV_SCORING\n    if (g.NavMoveFlags & ImGuiNavMoveFlags_DebugNoResult) // [DEBUG] Scoring all items in NavWindow at all times\n        return;\n#endif\n\n    // Select which result to use\n    ImGuiNavItemData* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : (g.NavMoveResultOther.ID != 0) ? &g.NavMoveResultOther : NULL;\n\n    // Tabbing forward wrap\n    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && result == NULL)\n        if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID)\n            result = &g.NavTabbingResultFirst;\n\n    // In a situation when there are no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result)\n    const ImGuiAxis axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;\n    if (result == NULL)\n    {\n        if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)\n            g.NavMoveFlags |= ImGuiNavMoveFlags_NoSetNavHighlight;\n        if (g.NavId != 0 && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0)\n            NavRestoreHighlightAfterMove();\n        NavClearPreferredPosForAxis(axis); // On a failed move, clear preferred pos for this axis.\n        IMGUI_DEBUG_LOG_NAV(\"[nav] NavMoveSubmitted but not led to a result!\\n\");\n        return;\n    }\n\n    // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page.\n    if (g.NavMoveFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet)\n        if (g.NavMoveResultLocalVisible.ID != 0 && g.NavMoveResultLocalVisible.ID != g.NavId)\n            result = &g.NavMoveResultLocalVisible;\n\n    // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules.\n    if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow)\n        if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter))\n            result = &g.NavMoveResultOther;\n    IM_ASSERT(g.NavWindow && result->Window);\n\n    // Scroll to keep newly navigated item fully into view.\n    if (g.NavLayer == ImGuiNavLayer_Main)\n    {\n        ImRect rect_abs = WindowRectRelToAbs(result->Window, result->RectRel);\n        ScrollToRectEx(result->Window, rect_abs, g.NavMoveScrollFlags);\n\n        if (g.NavMoveFlags & ImGuiNavMoveFlags_ScrollToEdgeY)\n        {\n            // FIXME: Should remove this? Or make more precise: use ScrollToRectEx() with edge?\n            float scroll_target = (g.NavMoveDir == ImGuiDir_Up) ? result->Window->ScrollMax.y : 0.0f;\n            SetScrollY(result->Window, scroll_target);\n        }\n    }\n\n    if (g.NavWindow != result->Window)\n    {\n        IMGUI_DEBUG_LOG_FOCUS(\"[focus] NavMoveRequest: SetNavWindow(\\\"%s\\\")\\n\", result->Window->Name);\n        g.NavWindow = result->Window;\n        g.NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;\n    }\n    if (g.ActiveId != result->ID)\n        ClearActiveID();\n\n    // Don't set NavJustMovedToId if just landed on the same spot (which may happen with ImGuiNavMoveFlags_AllowCurrentNavId)\n    // PageUp/PageDown however sets always set NavJustMovedTo (vs Home/End which doesn't) mimicking Windows behavior.\n    if ((g.NavId != result->ID || (g.NavMoveFlags & ImGuiNavMoveFlags_IsPageMove)) && (g.NavMoveFlags & ImGuiNavMoveFlags_NoSelect) == 0)\n    {\n        g.NavJustMovedToId = result->ID;\n        g.NavJustMovedToFocusScopeId = result->FocusScopeId;\n        g.NavJustMovedToKeyMods = g.NavMoveKeyMods;\n    }\n\n    // Apply new NavID/Focus\n    IMGUI_DEBUG_LOG_NAV(\"[nav] NavMoveRequest: result NavID 0x%08X in Layer %d Window \\\"%s\\\"\\n\", result->ID, g.NavLayer, g.NavWindow->Name);\n    ImVec2 preferred_scoring_pos_rel = g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer];\n    SetNavID(result->ID, g.NavLayer, result->FocusScopeId, result->RectRel);\n    if (result->SelectionUserData != ImGuiSelectionUserData_Invalid)\n        g.NavLastValidSelectionUserData = result->SelectionUserData;\n\n    // Restore last preferred position for current axis\n    // (storing in RootWindowForNav-> as the info is desirable at the beginning of a Move Request. In theory all storage should use RootWindowForNav..)\n    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) == 0)\n    {\n        preferred_scoring_pos_rel[axis] = result->RectRel.GetCenter()[axis];\n        g.NavWindow->RootWindowForNav->NavPreferredScoringPosRel[g.NavLayer] = preferred_scoring_pos_rel;\n    }\n\n    // Tabbing: Activates Inputable, otherwise only Focus\n    if ((g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing) && (result->InFlags & ImGuiItemFlags_Inputable) == 0)\n        g.NavMoveFlags &= ~ImGuiNavMoveFlags_Activate;\n\n    // Activate\n    if (g.NavMoveFlags & ImGuiNavMoveFlags_Activate)\n    {\n        g.NavNextActivateId = result->ID;\n        g.NavNextActivateFlags = ImGuiActivateFlags_None;\n        if (g.NavMoveFlags & ImGuiNavMoveFlags_IsTabbing)\n            g.NavNextActivateFlags |= ImGuiActivateFlags_PreferInput | ImGuiActivateFlags_TryToPreserveState;\n    }\n\n    // Enable nav highlight\n    if ((g.NavMoveFlags & ImGuiNavMoveFlags_NoSetNavHighlight) == 0)\n        NavRestoreHighlightAfterMove();\n}\n\n// Process NavCancel input (to close a popup, get back to parent, clear focus)\n// FIXME: In order to support e.g. Escape to clear a selection we'll need:\n// - either to store the equivalent of ActiveIdUsingKeyInputMask for a FocusScope and test for it.\n// - either to move most/all of those tests to the epilogue/end functions of the scope they are dealing with (e.g. exit child window in EndChild()) or in EndFrame(), to allow an earlier intercept\nstatic void ImGui::NavUpdateCancelRequest()\n{\n    ImGuiContext& g = *GImGui;\n    const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;\n    const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n    if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, ImGuiKeyOwner_None)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, ImGuiKeyOwner_None)))\n        return;\n\n    IMGUI_DEBUG_LOG_NAV(\"[nav] NavUpdateCancelRequest()\\n\");\n    if (g.ActiveId != 0)\n    {\n        ClearActiveID();\n    }\n    else if (g.NavLayer != ImGuiNavLayer_Main)\n    {\n        // Leave the \"menu\" layer\n        NavRestoreLayer(ImGuiNavLayer_Main);\n        NavRestoreHighlightAfterMove();\n    }\n    else if (g.NavWindow && g.NavWindow != g.NavWindow->RootWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)\n    {\n        // Exit child window\n        ImGuiWindow* child_window = g.NavWindow;\n        ImGuiWindow* parent_window = g.NavWindow->ParentWindow;\n        IM_ASSERT(child_window->ChildId != 0);\n        ImRect child_rect = child_window->Rect();\n        FocusWindow(parent_window);\n        SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect));\n        NavRestoreHighlightAfterMove();\n    }\n    else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal))\n    {\n        // Close open popup/menu\n        ClosePopupToLevel(g.OpenPopupStack.Size - 1, true);\n    }\n    else\n    {\n        // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were\n        if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow)))\n            g.NavWindow->NavLastIds[0] = 0;\n        g.NavId = 0;\n    }\n}\n\n// Handle PageUp/PageDown/Home/End keys\n// Called from NavUpdateCreateMoveRequest() which will use our output to create a move request\n// FIXME-NAV: This doesn't work properly with NavFlattened siblings as we use NavWindow rectangle for reference\n// FIXME-NAV: how to get Home/End to aim at the beginning/end of a 2D grid?\nstatic float ImGui::NavUpdatePageUpPageDown()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.NavWindow;\n    if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL)\n        return 0.0f;\n\n    const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_None);\n    const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_None);\n    const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat);\n    const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat);\n    if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out\n        return 0.0f;\n\n    if (g.NavLayer != ImGuiNavLayer_Main)\n        NavRestoreLayer(ImGuiNavLayer_Main);\n\n    if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY)\n    {\n        // Fallback manual-scroll when window has no navigable item\n        if (IsKeyPressed(ImGuiKey_PageUp, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat))\n            SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight());\n        else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat))\n            SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight());\n        else if (home_pressed)\n            SetScrollY(window, 0.0f);\n        else if (end_pressed)\n            SetScrollY(window, window->ScrollMax.y);\n    }\n    else\n    {\n        ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer];\n        const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight());\n        float nav_scoring_rect_offset_y = 0.0f;\n        if (IsKeyPressed(ImGuiKey_PageUp, true))\n        {\n            nav_scoring_rect_offset_y = -page_offset_y;\n            g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset up, we request the down direction (so we can always land on the last item)\n            g.NavMoveClipDir = ImGuiDir_Up;\n            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;\n        }\n        else if (IsKeyPressed(ImGuiKey_PageDown, true))\n        {\n            nav_scoring_rect_offset_y = +page_offset_y;\n            g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset down, we request the up direction (so we can always land on the last item)\n            g.NavMoveClipDir = ImGuiDir_Down;\n            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet | ImGuiNavMoveFlags_IsPageMove;\n        }\n        else if (home_pressed)\n        {\n            // FIXME-NAV: handling of Home/End is assuming that the top/bottom most item will be visible with Scroll.y == 0/ScrollMax.y\n            // Scrolling will be handled via the ImGuiNavMoveFlags_ScrollToEdgeY flag, we don't scroll immediately to avoid scrolling happening before nav result.\n            // Preserve current horizontal position if we have any.\n            nav_rect_rel.Min.y = nav_rect_rel.Max.y = 0.0f;\n            if (nav_rect_rel.IsInverted())\n                nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;\n            g.NavMoveDir = ImGuiDir_Down;\n            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;\n            // FIXME-NAV: MoveClipDir left to _None, intentional?\n        }\n        else if (end_pressed)\n        {\n            nav_rect_rel.Min.y = nav_rect_rel.Max.y = window->ContentSize.y;\n            if (nav_rect_rel.IsInverted())\n                nav_rect_rel.Min.x = nav_rect_rel.Max.x = 0.0f;\n            g.NavMoveDir = ImGuiDir_Up;\n            g.NavMoveFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_ScrollToEdgeY;\n            // FIXME-NAV: MoveClipDir left to _None, intentional?\n        }\n        return nav_scoring_rect_offset_y;\n    }\n    return 0.0f;\n}\n\nstatic void ImGui::NavEndFrame()\n{\n    ImGuiContext& g = *GImGui;\n\n    // Show CTRL+TAB list window\n    if (g.NavWindowingTarget != NULL)\n        NavUpdateWindowingOverlay();\n\n    // Perform wrap-around in menus\n    // FIXME-NAV: Wrap may need to apply a weight bias on the other axis. e.g. 4x4 grid with 2 last items missing on last item won't handle LoopY/WrapY correctly.\n    // FIXME-NAV: Wrap (not Loop) support could be handled by the scoring function and then WrapX would function without an extra frame.\n    if (g.NavWindow && NavMoveRequestButNoResultYet() && (g.NavMoveFlags & ImGuiNavMoveFlags_WrapMask_) && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)\n        NavUpdateCreateWrappingRequest();\n}\n\nstatic void ImGui::NavUpdateCreateWrappingRequest()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.NavWindow;\n\n    bool do_forward = false;\n    ImRect bb_rel = window->NavRectRel[g.NavLayer];\n    ImGuiDir clip_dir = g.NavMoveDir;\n\n    const ImGuiNavMoveFlags move_flags = g.NavMoveFlags;\n    //const ImGuiAxis move_axis = (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;\n    if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))\n    {\n        bb_rel.Min.x = bb_rel.Max.x = window->ContentSize.x + window->WindowPadding.x;\n        if (move_flags & ImGuiNavMoveFlags_WrapX)\n        {\n            bb_rel.TranslateY(-bb_rel.GetHeight()); // Previous row\n            clip_dir = ImGuiDir_Up;\n        }\n        do_forward = true;\n    }\n    if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX)))\n    {\n        bb_rel.Min.x = bb_rel.Max.x = -window->WindowPadding.x;\n        if (move_flags & ImGuiNavMoveFlags_WrapX)\n        {\n            bb_rel.TranslateY(+bb_rel.GetHeight()); // Next row\n            clip_dir = ImGuiDir_Down;\n        }\n        do_forward = true;\n    }\n    if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))\n    {\n        bb_rel.Min.y = bb_rel.Max.y = window->ContentSize.y + window->WindowPadding.y;\n        if (move_flags & ImGuiNavMoveFlags_WrapY)\n        {\n            bb_rel.TranslateX(-bb_rel.GetWidth()); // Previous column\n            clip_dir = ImGuiDir_Left;\n        }\n        do_forward = true;\n    }\n    if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY)))\n    {\n        bb_rel.Min.y = bb_rel.Max.y = -window->WindowPadding.y;\n        if (move_flags & ImGuiNavMoveFlags_WrapY)\n        {\n            bb_rel.TranslateX(+bb_rel.GetWidth()); // Next column\n            clip_dir = ImGuiDir_Right;\n        }\n        do_forward = true;\n    }\n    if (!do_forward)\n        return;\n    window->NavRectRel[g.NavLayer] = bb_rel;\n    NavClearPreferredPosForAxis(ImGuiAxis_X);\n    NavClearPreferredPosForAxis(ImGuiAxis_Y);\n    NavMoveRequestForward(g.NavMoveDir, clip_dir, move_flags, g.NavMoveScrollFlags);\n}\n\nstatic int ImGui::FindWindowFocusIndex(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    IM_UNUSED(g);\n    int order = window->FocusOrder;\n    IM_ASSERT(window->RootWindow == window); // No child window (not testing _ChildWindow because of docking)\n    IM_ASSERT(g.WindowsFocusOrder[order] == window);\n    return order;\n}\n\nstatic ImGuiWindow* FindWindowNavFocusable(int i_start, int i_stop, int dir) // FIXME-OPT O(N)\n{\n    ImGuiContext& g = *GImGui;\n    for (int i = i_start; i >= 0 && i < g.WindowsFocusOrder.Size && i != i_stop; i += dir)\n        if (ImGui::IsWindowNavFocusable(g.WindowsFocusOrder[i]))\n            return g.WindowsFocusOrder[i];\n    return NULL;\n}\n\nstatic void NavUpdateWindowingHighlightWindow(int focus_change_dir)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.NavWindowingTarget);\n    if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal)\n        return;\n\n    const int i_current = ImGui::FindWindowFocusIndex(g.NavWindowingTarget);\n    ImGuiWindow* window_target = FindWindowNavFocusable(i_current + focus_change_dir, -INT_MAX, focus_change_dir);\n    if (!window_target)\n        window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir);\n    if (window_target) // Don't reset windowing target if there's a single window in the list\n    {\n        g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target;\n        g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);\n    }\n    g.NavWindowingToggleLayer = false;\n}\n\n// Windowing management mode\n// Keyboard: CTRL+Tab (change focus/move/resize), Alt (toggle menu layer)\n// Gamepad:  Hold Menu/Square (change focus/move/resize), Tap Menu/Square (toggle menu layer)\nstatic void ImGui::NavUpdateWindowing()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n\n    ImGuiWindow* apply_focus_window = NULL;\n    bool apply_toggle_layer = false;\n\n    ImGuiWindow* modal_window = GetTopMostPopupModal();\n    bool allow_windowing = (modal_window == NULL); // FIXME: This prevent CTRL+TAB from being usable with windows that are inside the Begin-stack of that modal.\n    if (!allow_windowing)\n        g.NavWindowingTarget = NULL;\n\n    // Fade out\n    if (g.NavWindowingTargetAnim && g.NavWindowingTarget == NULL)\n    {\n        g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha - io.DeltaTime * 10.0f, 0.0f);\n        if (g.DimBgRatio <= 0.0f && g.NavWindowingHighlightAlpha <= 0.0f)\n            g.NavWindowingTargetAnim = NULL;\n    }\n\n    // Start CTRL+Tab or Square+L/R window selection\n    const ImGuiID owner_id = ImHashStr(\"###NavUpdateWindowing\");\n    const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;\n    const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0;\n    const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, owner_id, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways);\n    const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, owner_id, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways);\n    const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, 0, ImGuiInputFlags_None);\n    const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard!\n    if (start_windowing_with_gamepad || start_windowing_with_keyboard)\n        if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1))\n        {\n            g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow;\n            g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f;\n            g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f);\n            g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer\n            g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad;\n\n            // Register ownership of our mods. Using ImGuiInputFlags_RouteGlobalHigh in the Shortcut() calls instead would probably be correct but may have more side-effects.\n            if (keyboard_next_window || keyboard_prev_window)\n                SetKeyOwnersForKeyChord((g.ConfigNavWindowingKeyNext | g.ConfigNavWindowingKeyPrev) & ImGuiMod_Mask_, owner_id);\n        }\n\n    // Gamepad update\n    g.NavWindowingTimer += io.DeltaTime;\n    if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Gamepad)\n    {\n        // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise\n        g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f));\n\n        // Select window to focus\n        const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1);\n        if (focus_change_dir != 0)\n        {\n            NavUpdateWindowingHighlightWindow(focus_change_dir);\n            g.NavWindowingHighlightAlpha = 1.0f;\n        }\n\n        // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most)\n        if (!IsKeyDown(ImGuiKey_NavGamepadMenu))\n        {\n            g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore.\n            if (g.NavWindowingToggleLayer && g.NavWindow)\n                apply_toggle_layer = true;\n            else if (!g.NavWindowingToggleLayer)\n                apply_focus_window = g.NavWindowingTarget;\n            g.NavWindowingTarget = NULL;\n        }\n    }\n\n    // Keyboard: Focus\n    if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard)\n    {\n        // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise\n        ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_;\n        IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to \"hold\", otherwise Prev actions would keep cycling between two windows.\n        g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f\n        if (keyboard_next_window || keyboard_prev_window)\n            NavUpdateWindowingHighlightWindow(keyboard_next_window ? -1 : +1);\n        else if ((io.KeyMods & shared_mods) != shared_mods)\n            apply_focus_window = g.NavWindowingTarget;\n    }\n\n    // Keyboard: Press and Release ALT to toggle menu layer\n    // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer.\n    // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway.\n    if (nav_keyboard_active && IsKeyPressed(ImGuiMod_Alt, ImGuiKeyOwner_None))\n    {\n        g.NavWindowingToggleLayer = true;\n        g.NavInputSource = ImGuiInputSource_Keyboard;\n    }\n    if (g.NavWindowingToggleLayer && g.NavInputSource == ImGuiInputSource_Keyboard)\n    {\n        // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370)\n        // We cancel toggling nav layer when other modifiers are pressed. (See #4439)\n        // We cancel toggling nav layer if an owner has claimed the key.\n        if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_None) == false)\n            g.NavWindowingToggleLayer = false;\n\n        // Apply layer toggle on release\n        // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss.\n        if (IsKeyReleased(ImGuiMod_Alt) && g.NavWindowingToggleLayer)\n            if (g.ActiveId == 0 || g.ActiveIdAllowOverlap)\n                if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev))\n                    apply_toggle_layer = true;\n        if (!IsKeyDown(ImGuiMod_Alt))\n            g.NavWindowingToggleLayer = false;\n    }\n\n    // Move window\n    if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove))\n    {\n        ImVec2 nav_move_dir;\n        if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift)\n            nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow);\n        if (g.NavInputSource == ImGuiInputSource_Gamepad)\n            nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown);\n        if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f)\n        {\n            const float NAV_MOVE_SPEED = 800.0f;\n            const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);\n            g.NavWindowingAccumDeltaPos += nav_move_dir * move_step;\n            g.NavDisableMouseHover = true;\n            ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos);\n            if (accum_floored.x != 0.0f || accum_floored.y != 0.0f)\n            {\n                ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow;\n                SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always);\n                g.NavWindowingAccumDeltaPos -= accum_floored;\n            }\n        }\n    }\n\n    // Apply final focus\n    if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))\n    {\n        ClearActiveID();\n        NavRestoreHighlightAfterMove();\n        ClosePopupsOverWindow(apply_focus_window, false);\n        FocusWindow(apply_focus_window, ImGuiFocusRequestFlags_RestoreFocusedChild);\n        apply_focus_window = g.NavWindow;\n        if (apply_focus_window->NavLastIds[0] == 0)\n            NavInitWindow(apply_focus_window, false);\n\n        // If the window has ONLY a menu layer (no main layer), select it directly\n        // Use NavLayersActiveMaskNext since windows didn't have a chance to be Begin()-ed on this frame,\n        // so CTRL+Tab where the keys are only held for 1 frame will be able to use correct layers mask since\n        // the target window as already been previewed once.\n        // FIXME-NAV: This should be done in NavInit.. or in FocusWindow... However in both of those cases,\n        // we won't have a guarantee that windows has been visible before and therefore NavLayersActiveMask*\n        // won't be valid.\n        if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu))\n            g.NavLayer = ImGuiNavLayer_Menu;\n    }\n    if (apply_focus_window)\n        g.NavWindowingTarget = NULL;\n\n    // Apply menu/layer toggle\n    if (apply_toggle_layer && g.NavWindow)\n    {\n        ClearActiveID();\n\n        // Move to parent menu if necessary\n        ImGuiWindow* new_nav_window = g.NavWindow;\n        while (new_nav_window->ParentWindow\n            && (new_nav_window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0\n            && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0\n            && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)\n            new_nav_window = new_nav_window->ParentWindow;\n        if (new_nav_window != g.NavWindow)\n        {\n            ImGuiWindow* old_nav_window = g.NavWindow;\n            FocusWindow(new_nav_window);\n            new_nav_window->NavLastChildNavWindow = old_nav_window;\n        }\n\n        // Toggle layer\n        const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main;\n        if (new_nav_layer != g.NavLayer)\n        {\n            // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?)\n            if (new_nav_layer == ImGuiNavLayer_Menu)\n                g.NavWindow->NavLastIds[new_nav_layer] = 0;\n            NavRestoreLayer(new_nav_layer);\n            NavRestoreHighlightAfterMove();\n        }\n    }\n}\n\n// Window has already passed the IsWindowNavFocusable()\nstatic const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)\n{\n    if (window->Flags & ImGuiWindowFlags_Popup)\n        return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup);\n    if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, \"##MainMenuBar\") == 0)\n        return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar);\n    return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled);\n}\n\n// Overlay displayed when using CTRL+TAB. Called by EndFrame().\nvoid ImGui::NavUpdateWindowingOverlay()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.NavWindowingTarget != NULL);\n\n    if (g.NavWindowingTimer < NAV_WINDOWING_LIST_APPEAR_DELAY)\n        return;\n\n    if (g.NavWindowingListWindow == NULL)\n        g.NavWindowingListWindow = FindWindowByName(\"###NavWindowingList\");\n    const ImGuiViewport* viewport = GetMainViewport();\n    SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX));\n    SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));\n    PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f);\n    Begin(\"###NavWindowingList\", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings);\n    for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--)\n    {\n        ImGuiWindow* window = g.WindowsFocusOrder[n];\n        IM_ASSERT(window != NULL); // Fix static analyzers\n        if (!IsWindowNavFocusable(window))\n            continue;\n        const char* label = window->Name;\n        if (label == FindRenderedTextEnd(label))\n            label = GetFallbackWindowNameForWindowingList(window);\n        Selectable(label, g.NavWindowingTarget == window);\n    }\n    End();\n    PopStyleVar();\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] DRAG AND DROP\n//-----------------------------------------------------------------------------\n\nbool ImGui::IsDragDropActive()\n{\n    ImGuiContext& g = *GImGui;\n    return g.DragDropActive;\n}\n\nvoid ImGui::ClearDragDrop()\n{\n    ImGuiContext& g = *GImGui;\n    g.DragDropActive = false;\n    g.DragDropPayload.Clear();\n    g.DragDropAcceptFlags = ImGuiDragDropFlags_None;\n    g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0;\n    g.DragDropAcceptIdCurrRectSurface = FLT_MAX;\n    g.DragDropAcceptFrameCount = -1;\n\n    g.DragDropPayloadBufHeap.clear();\n    memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));\n}\n\nbool ImGui::BeginTooltipHidden()\n{\n    ImGuiContext& g = *GImGui;\n    bool ret = Begin(\"##Tooltip_Hidden\", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize);\n    SetWindowHiddendAndSkipItemsForCurrentFrame(g.CurrentWindow);\n    return ret;\n}\n\n// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource()\n// If the item has an identifier:\n// - This assume/require the item to be activated (typically via ButtonBehavior).\n// - Therefore if you want to use this with a mouse button other than left mouse button, it is up to the item itself to activate with another button.\n// - We then pull and use the mouse button that was used to activate the item and use it to carry on the drag.\n// If the item has no identifier:\n// - Currently always assume left mouse button.\nbool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // FIXME-DRAGDROP: While in the common-most \"drag from non-zero active id\" case we can tell the mouse button,\n    // in both SourceExtern and id==0 cases we may requires something else (explicit flags or some heuristic).\n    ImGuiMouseButton mouse_button = ImGuiMouseButton_Left;\n\n    bool source_drag_active = false;\n    ImGuiID source_id = 0;\n    ImGuiID source_parent_id = 0;\n    if (!(flags & ImGuiDragDropFlags_SourceExtern))\n    {\n        source_id = g.LastItemData.ID;\n        if (source_id != 0)\n        {\n            // Common path: items with ID\n            if (g.ActiveId != source_id)\n                return false;\n            if (g.ActiveIdMouseButton != -1)\n                mouse_button = g.ActiveIdMouseButton;\n            if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)\n                return false;\n            g.ActiveIdAllowOverlap = false;\n        }\n        else\n        {\n            // Uncommon path: items without ID\n            if (g.IO.MouseDown[mouse_button] == false || window->SkipItems)\n                return false;\n            if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) == 0 && (g.ActiveId == 0 || g.ActiveIdWindow != window))\n                return false;\n\n            // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to:\n            // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag.\n            if (!(flags & ImGuiDragDropFlags_SourceAllowNullID))\n            {\n                IM_ASSERT(0);\n                return false;\n            }\n\n            // Magic fallback to handle items with no assigned ID, e.g. Text(), Image()\n            // We build a throwaway ID based on current ID stack + relative AABB of items in window.\n            // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING/RESIZINGG OF THE WIDGET, so if your widget moves your dragging operation will be canceled.\n            // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive.\n            // Rely on keeping other window->LastItemXXX fields intact.\n            source_id = g.LastItemData.ID = window->GetIDFromRectangle(g.LastItemData.Rect);\n            KeepAliveID(source_id);\n            bool is_hovered = ItemHoverable(g.LastItemData.Rect, source_id, g.LastItemData.InFlags);\n            if (is_hovered && g.IO.MouseClicked[mouse_button])\n            {\n                SetActiveID(source_id, window);\n                FocusWindow(window);\n            }\n            if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker.\n                g.ActiveIdAllowOverlap = is_hovered;\n        }\n        if (g.ActiveId != source_id)\n            return false;\n        source_parent_id = window->IDStack.back();\n        source_drag_active = IsMouseDragging(mouse_button);\n\n        // Disable navigation and key inputs while dragging + cancel existing request if any\n        SetActiveIdUsingAllKeyboardKeys();\n    }\n    else\n    {\n        window = NULL;\n        source_id = ImHashStr(\"#SourceExtern\");\n        source_drag_active = true;\n    }\n\n    if (source_drag_active)\n    {\n        if (!g.DragDropActive)\n        {\n            IM_ASSERT(source_id != 0);\n            ClearDragDrop();\n            ImGuiPayload& payload = g.DragDropPayload;\n            payload.SourceId = source_id;\n            payload.SourceParentId = source_parent_id;\n            g.DragDropActive = true;\n            g.DragDropSourceFlags = flags;\n            g.DragDropMouseButton = mouse_button;\n            if (payload.SourceId == g.ActiveId)\n                g.ActiveIdNoClearOnFocusLoss = true;\n        }\n        g.DragDropSourceFrameCount = g.FrameCount;\n        g.DragDropWithinSource = true;\n\n        if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip))\n        {\n            // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit)\n            // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents.\n            bool ret;\n            if (g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip))\n                ret = BeginTooltipHidden();\n            else\n                ret = BeginTooltip();\n            IM_ASSERT(ret); // FIXME-NEWBEGIN: If this ever becomes false, we need to Begin(\"##Hidden\", NULL, ImGuiWindowFlags_NoSavedSettings) + SetWindowHiddendAndSkipItemsForCurrentFrame().\n            IM_UNUSED(ret);\n        }\n\n        if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern))\n            g.LastItemData.StatusFlags &= ~ImGuiItemStatusFlags_HoveredRect;\n\n        return true;\n    }\n    return false;\n}\n\nvoid ImGui::EndDragDropSource()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.DragDropActive);\n    IM_ASSERT(g.DragDropWithinSource && \"Not after a BeginDragDropSource()?\");\n\n    if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip))\n        EndTooltip();\n\n    // Discard the drag if have not called SetDragDropPayload()\n    if (g.DragDropPayload.DataFrameCount == -1)\n        ClearDragDrop();\n    g.DragDropWithinSource = false;\n}\n\n// Use 'cond' to choose to submit payload on drag start or every frame\nbool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiPayload& payload = g.DragDropPayload;\n    if (cond == 0)\n        cond = ImGuiCond_Always;\n\n    IM_ASSERT(type != NULL);\n    IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && \"Payload type can be at most 32 characters long\");\n    IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0));\n    IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once);\n    IM_ASSERT(payload.SourceId != 0);                               // Not called between BeginDragDropSource() and EndDragDropSource()\n\n    if (cond == ImGuiCond_Always || payload.DataFrameCount == -1)\n    {\n        // Copy payload\n        ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType));\n        g.DragDropPayloadBufHeap.resize(0);\n        if (data_size > sizeof(g.DragDropPayloadBufLocal))\n        {\n            // Store in heap\n            g.DragDropPayloadBufHeap.resize((int)data_size);\n            payload.Data = g.DragDropPayloadBufHeap.Data;\n            memcpy(payload.Data, data, data_size);\n        }\n        else if (data_size > 0)\n        {\n            // Store locally\n            memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal));\n            payload.Data = g.DragDropPayloadBufLocal;\n            memcpy(payload.Data, data, data_size);\n        }\n        else\n        {\n            payload.Data = NULL;\n        }\n        payload.DataSize = (int)data_size;\n    }\n    payload.DataFrameCount = g.FrameCount;\n\n    // Return whether the payload has been accepted\n    return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1);\n}\n\nbool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    if (!g.DragDropActive)\n        return false;\n\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;\n    if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow)\n        return false;\n    IM_ASSERT(id != 0);\n    if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))\n        return false;\n    if (window->SkipItems)\n        return false;\n\n    IM_ASSERT(g.DragDropWithinTarget == false);\n    g.DragDropTargetRect = bb;\n    g.DragDropTargetId = id;\n    g.DragDropWithinTarget = true;\n    return true;\n}\n\n// We don't use BeginDragDropTargetCustom() and duplicate its code because:\n// 1) we use LastItemRectHoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them.\n// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can.\n// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case)\nbool ImGui::BeginDragDropTarget()\n{\n    ImGuiContext& g = *GImGui;\n    if (!g.DragDropActive)\n        return false;\n\n    ImGuiWindow* window = g.CurrentWindow;\n    if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect))\n        return false;\n    ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow;\n    if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems)\n        return false;\n\n    const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect;\n    ImGuiID id = g.LastItemData.ID;\n    if (id == 0)\n    {\n        id = window->GetIDFromRectangle(display_rect);\n        KeepAliveID(id);\n    }\n    if (g.DragDropPayload.SourceId == id)\n        return false;\n\n    IM_ASSERT(g.DragDropWithinTarget == false);\n    g.DragDropTargetRect = display_rect;\n    g.DragDropTargetId = id;\n    g.DragDropWithinTarget = true;\n    return true;\n}\n\nbool ImGui::IsDragDropPayloadBeingAccepted()\n{\n    ImGuiContext& g = *GImGui;\n    return g.DragDropActive && g.DragDropAcceptIdPrev != 0;\n}\n\nconst ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiPayload& payload = g.DragDropPayload;\n    IM_ASSERT(g.DragDropActive);                        // Not called between BeginDragDropTarget() and EndDragDropTarget() ?\n    IM_ASSERT(payload.DataFrameCount != -1);            // Forgot to call EndDragDropTarget() ?\n    if (type != NULL && !payload.IsDataType(type))\n        return NULL;\n\n    // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints.\n    // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function!\n    const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId);\n    ImRect r = g.DragDropTargetRect;\n    float r_surface = r.GetWidth() * r.GetHeight();\n    if (r_surface > g.DragDropAcceptIdCurrRectSurface)\n        return NULL;\n\n    g.DragDropAcceptFlags = flags;\n    g.DragDropAcceptIdCurr = g.DragDropTargetId;\n    g.DragDropAcceptIdCurrRectSurface = r_surface;\n    //IMGUI_DEBUG_LOG(\"AcceptDragDropPayload(): %08X: accept\\n\", g.DragDropTargetId);\n\n    // Render default drop visuals\n    payload.Preview = was_accepted_previously;\n    flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame)\n    if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview)\n        RenderDragDropTargetRect(r);\n\n    g.DragDropAcceptFrameCount = g.FrameCount;\n    payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting OS window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased()\n    if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery))\n        return NULL;\n\n    //IMGUI_DEBUG_LOG(\"AcceptDragDropPayload(): %08X: return payload\\n\", g.DragDropTargetId);\n    return &payload;\n}\n\n// FIXME-STYLE FIXME-DRAGDROP: Settle on a proper default visuals for drop target.\nvoid ImGui::RenderDragDropTargetRect(const ImRect& bb)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImRect bb_display = bb;\n    bb_display.ClipWith(window->ClipRect); // Clip THEN expand so we have a way to visualize that target is not entirely visible.\n    bb_display.Expand(3.5f);\n    bool push_clip_rect = !window->ClipRect.Contains(bb_display);\n    if (push_clip_rect)\n        window->DrawList->PushClipRectFullScreen();\n    window->DrawList->AddRect(bb_display.Min, bb_display.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f);\n    if (push_clip_rect)\n        window->DrawList->PopClipRect();\n}\n\nconst ImGuiPayload* ImGui::GetDragDropPayload()\n{\n    ImGuiContext& g = *GImGui;\n    return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL;\n}\n\nvoid ImGui::EndDragDropTarget()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.DragDropActive);\n    IM_ASSERT(g.DragDropWithinTarget);\n    g.DragDropWithinTarget = false;\n\n    // Clear drag and drop state payload right after delivery\n    if (g.DragDropPayload.Delivery)\n        ClearDragDrop();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] LOGGING/CAPTURING\n//-----------------------------------------------------------------------------\n// All text output from the interface can be captured into tty/file/clipboard.\n// By default, tree nodes are automatically opened during logging.\n//-----------------------------------------------------------------------------\n\n// Pass text data straight to log (without being displayed)\nstatic inline void LogTextV(ImGuiContext& g, const char* fmt, va_list args)\n{\n    if (g.LogFile)\n    {\n        g.LogBuffer.Buf.resize(0);\n        g.LogBuffer.appendfv(fmt, args);\n        ImFileWrite(g.LogBuffer.c_str(), sizeof(char), (ImU64)g.LogBuffer.size(), g.LogFile);\n    }\n    else\n    {\n        g.LogBuffer.appendfv(fmt, args);\n    }\n}\n\nvoid ImGui::LogText(const char* fmt, ...)\n{\n    ImGuiContext& g = *GImGui;\n    if (!g.LogEnabled)\n        return;\n\n    va_list args;\n    va_start(args, fmt);\n    LogTextV(g, fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::LogTextV(const char* fmt, va_list args)\n{\n    ImGuiContext& g = *GImGui;\n    if (!g.LogEnabled)\n        return;\n\n    LogTextV(g, fmt, args);\n}\n\n// Internal version that takes a position to decide on newline placement and pad items according to their depth.\n// We split text into individual lines to add current tree level padding\n// FIXME: This code is a little complicated perhaps, considering simplifying the whole system.\nvoid ImGui::LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    const char* prefix = g.LogNextPrefix;\n    const char* suffix = g.LogNextSuffix;\n    g.LogNextPrefix = g.LogNextSuffix = NULL;\n\n    if (!text_end)\n        text_end = FindRenderedTextEnd(text, text_end);\n\n    const bool log_new_line = ref_pos && (ref_pos->y > g.LogLinePosY + g.Style.FramePadding.y + 1);\n    if (ref_pos)\n        g.LogLinePosY = ref_pos->y;\n    if (log_new_line)\n    {\n        LogText(IM_NEWLINE);\n        g.LogLineFirstItem = true;\n    }\n\n    if (prefix)\n        LogRenderedText(ref_pos, prefix, prefix + strlen(prefix)); // Calculate end ourself to ensure \"##\" are included here.\n\n    // Re-adjust padding if we have popped out of our starting depth\n    if (g.LogDepthRef > window->DC.TreeDepth)\n        g.LogDepthRef = window->DC.TreeDepth;\n    const int tree_depth = (window->DC.TreeDepth - g.LogDepthRef);\n\n    const char* text_remaining = text;\n    for (;;)\n    {\n        // Split the string. Each new line (after a '\\n') is followed by indentation corresponding to the current depth of our log entry.\n        // We don't add a trailing \\n yet to allow a subsequent item on the same line to be captured.\n        const char* line_start = text_remaining;\n        const char* line_end = ImStreolRange(line_start, text_end);\n        const bool is_last_line = (line_end == text_end);\n        if (line_start != line_end || !is_last_line)\n        {\n            const int line_length = (int)(line_end - line_start);\n            const int indentation = g.LogLineFirstItem ? tree_depth * 4 : 1;\n            LogText(\"%*s%.*s\", indentation, \"\", line_length, line_start);\n            g.LogLineFirstItem = false;\n            if (*line_end == '\\n')\n            {\n                LogText(IM_NEWLINE);\n                g.LogLineFirstItem = true;\n            }\n        }\n        if (is_last_line)\n            break;\n        text_remaining = line_end + 1;\n    }\n\n    if (suffix)\n        LogRenderedText(ref_pos, suffix, suffix + strlen(suffix));\n}\n\n// Start logging/capturing text output\nvoid ImGui::LogBegin(ImGuiLogType type, int auto_open_depth)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(g.LogEnabled == false);\n    IM_ASSERT(g.LogFile == NULL);\n    IM_ASSERT(g.LogBuffer.empty());\n    g.LogEnabled = true;\n    g.LogType = type;\n    g.LogNextPrefix = g.LogNextSuffix = NULL;\n    g.LogDepthRef = window->DC.TreeDepth;\n    g.LogDepthToExpand = ((auto_open_depth >= 0) ? auto_open_depth : g.LogDepthToExpandDefault);\n    g.LogLinePosY = FLT_MAX;\n    g.LogLineFirstItem = true;\n}\n\n// Important: doesn't copy underlying data, use carefully (prefix/suffix must be in scope at the time of the next LogRenderedText)\nvoid ImGui::LogSetNextTextDecoration(const char* prefix, const char* suffix)\n{\n    ImGuiContext& g = *GImGui;\n    g.LogNextPrefix = prefix;\n    g.LogNextSuffix = suffix;\n}\n\nvoid ImGui::LogToTTY(int auto_open_depth)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.LogEnabled)\n        return;\n    IM_UNUSED(auto_open_depth);\n#ifndef IMGUI_DISABLE_TTY_FUNCTIONS\n    LogBegin(ImGuiLogType_TTY, auto_open_depth);\n    g.LogFile = stdout;\n#endif\n}\n\n// Start logging/capturing text output to given file\nvoid ImGui::LogToFile(int auto_open_depth, const char* filename)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.LogEnabled)\n        return;\n\n    // FIXME: We could probably open the file in text mode \"at\", however note that clipboard/buffer logging will still\n    // be subject to outputting OS-incompatible carriage return if within strings the user doesn't use IM_NEWLINE.\n    // By opening the file in binary mode \"ab\" we have consistent output everywhere.\n    if (!filename)\n        filename = g.IO.LogFilename;\n    if (!filename || !filename[0])\n        return;\n    ImFileHandle f = ImFileOpen(filename, \"ab\");\n    if (!f)\n    {\n        IM_ASSERT(0);\n        return;\n    }\n\n    LogBegin(ImGuiLogType_File, auto_open_depth);\n    g.LogFile = f;\n}\n\n// Start logging/capturing text output to clipboard\nvoid ImGui::LogToClipboard(int auto_open_depth)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.LogEnabled)\n        return;\n    LogBegin(ImGuiLogType_Clipboard, auto_open_depth);\n}\n\nvoid ImGui::LogToBuffer(int auto_open_depth)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.LogEnabled)\n        return;\n    LogBegin(ImGuiLogType_Buffer, auto_open_depth);\n}\n\nvoid ImGui::LogFinish()\n{\n    ImGuiContext& g = *GImGui;\n    if (!g.LogEnabled)\n        return;\n\n    LogText(IM_NEWLINE);\n    switch (g.LogType)\n    {\n    case ImGuiLogType_TTY:\n#ifndef IMGUI_DISABLE_TTY_FUNCTIONS\n        fflush(g.LogFile);\n#endif\n        break;\n    case ImGuiLogType_File:\n        ImFileClose(g.LogFile);\n        break;\n    case ImGuiLogType_Buffer:\n        break;\n    case ImGuiLogType_Clipboard:\n        if (!g.LogBuffer.empty())\n            SetClipboardText(g.LogBuffer.begin());\n        break;\n    case ImGuiLogType_None:\n        IM_ASSERT(0);\n        break;\n    }\n\n    g.LogEnabled = false;\n    g.LogType = ImGuiLogType_None;\n    g.LogFile = NULL;\n    g.LogBuffer.clear();\n}\n\n// Helper to display logging buttons\n// FIXME-OBSOLETE: We should probably obsolete this and let the user have their own helper (this is one of the oldest function alive!)\nvoid ImGui::LogButtons()\n{\n    ImGuiContext& g = *GImGui;\n\n    PushID(\"LogButtons\");\n#ifndef IMGUI_DISABLE_TTY_FUNCTIONS\n    const bool log_to_tty = Button(\"Log To TTY\"); SameLine();\n#else\n    const bool log_to_tty = false;\n#endif\n    const bool log_to_file = Button(\"Log To File\"); SameLine();\n    const bool log_to_clipboard = Button(\"Log To Clipboard\"); SameLine();\n    PushTabStop(false);\n    SetNextItemWidth(80.0f);\n    SliderInt(\"Default Depth\", &g.LogDepthToExpandDefault, 0, 9, NULL);\n    PopTabStop();\n    PopID();\n\n    // Start logging at the end of the function so that the buttons don't appear in the log\n    if (log_to_tty)\n        LogToTTY();\n    if (log_to_file)\n        LogToFile();\n    if (log_to_clipboard)\n        LogToClipboard();\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] SETTINGS\n//-----------------------------------------------------------------------------\n// - UpdateSettings() [Internal]\n// - MarkIniSettingsDirty() [Internal]\n// - FindSettingsHandler() [Internal]\n// - ClearIniSettings() [Internal]\n// - LoadIniSettingsFromDisk()\n// - LoadIniSettingsFromMemory()\n// - SaveIniSettingsToDisk()\n// - SaveIniSettingsToMemory()\n//-----------------------------------------------------------------------------\n// - CreateNewWindowSettings() [Internal]\n// - FindWindowSettingsByID() [Internal]\n// - FindWindowSettingsByWindow() [Internal]\n// - ClearWindowSettings() [Internal]\n// - WindowSettingsHandler_***() [Internal]\n//-----------------------------------------------------------------------------\n\n// Called by NewFrame()\nvoid ImGui::UpdateSettings()\n{\n    // Load settings on first frame (if not explicitly loaded manually before)\n    ImGuiContext& g = *GImGui;\n    if (!g.SettingsLoaded)\n    {\n        IM_ASSERT(g.SettingsWindows.empty());\n        if (g.IO.IniFilename)\n            LoadIniSettingsFromDisk(g.IO.IniFilename);\n        g.SettingsLoaded = true;\n    }\n\n    // Save settings (with a delay after the last modification, so we don't spam disk too much)\n    if (g.SettingsDirtyTimer > 0.0f)\n    {\n        g.SettingsDirtyTimer -= g.IO.DeltaTime;\n        if (g.SettingsDirtyTimer <= 0.0f)\n        {\n            if (g.IO.IniFilename != NULL)\n                SaveIniSettingsToDisk(g.IO.IniFilename);\n            else\n                g.IO.WantSaveIniSettings = true;  // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves.\n            g.SettingsDirtyTimer = 0.0f;\n        }\n    }\n}\n\nvoid ImGui::MarkIniSettingsDirty()\n{\n    ImGuiContext& g = *GImGui;\n    if (g.SettingsDirtyTimer <= 0.0f)\n        g.SettingsDirtyTimer = g.IO.IniSavingRate;\n}\n\nvoid ImGui::MarkIniSettingsDirty(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings))\n        if (g.SettingsDirtyTimer <= 0.0f)\n            g.SettingsDirtyTimer = g.IO.IniSavingRate;\n}\n\nvoid ImGui::AddSettingsHandler(const ImGuiSettingsHandler* handler)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(FindSettingsHandler(handler->TypeName) == NULL);\n    g.SettingsHandlers.push_back(*handler);\n}\n\nvoid ImGui::RemoveSettingsHandler(const char* type_name)\n{\n    ImGuiContext& g = *GImGui;\n    if (ImGuiSettingsHandler* handler = FindSettingsHandler(type_name))\n        g.SettingsHandlers.erase(handler);\n}\n\nImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiID type_hash = ImHashStr(type_name);\n    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n        if (handler.TypeHash == type_hash)\n            return &handler;\n    return NULL;\n}\n\n// Clear all settings (windows, tables, docking etc.)\nvoid ImGui::ClearIniSettings()\n{\n    ImGuiContext& g = *GImGui;\n    g.SettingsIniData.clear();\n    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n        if (handler.ClearAllFn != NULL)\n            handler.ClearAllFn(&g, &handler);\n}\n\nvoid ImGui::LoadIniSettingsFromDisk(const char* ini_filename)\n{\n    size_t file_data_size = 0;\n    char* file_data = (char*)ImFileLoadToMemory(ini_filename, \"rb\", &file_data_size);\n    if (!file_data)\n        return;\n    if (file_data_size > 0)\n        LoadIniSettingsFromMemory(file_data, (size_t)file_data_size);\n    IM_FREE(file_data);\n}\n\n// Zero-tolerance, no error reporting, cheap .ini parsing\n// Set ini_size==0 to let us use strlen(ini_data). Do not call this function with a 0 if your buffer is actually empty!\nvoid ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.Initialized);\n    //IM_ASSERT(!g.WithinFrameScope && \"Cannot be called between NewFrame() and EndFrame()\");\n    //IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0);\n\n    // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter).\n    // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy..\n    if (ini_size == 0)\n        ini_size = strlen(ini_data);\n    g.SettingsIniData.Buf.resize((int)ini_size + 1);\n    char* const buf = g.SettingsIniData.Buf.Data;\n    char* const buf_end = buf + ini_size;\n    memcpy(buf, ini_data, ini_size);\n    buf_end[0] = 0;\n\n    // Call pre-read handlers\n    // Some types will clear their data (e.g. dock information) some types will allow merge/override (window)\n    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n        if (handler.ReadInitFn != NULL)\n            handler.ReadInitFn(&g, &handler);\n\n    void* entry_data = NULL;\n    ImGuiSettingsHandler* entry_handler = NULL;\n\n    char* line_end = NULL;\n    for (char* line = buf; line < buf_end; line = line_end + 1)\n    {\n        // Skip new lines markers, then find end of the line\n        while (*line == '\\n' || *line == '\\r')\n            line++;\n        line_end = line;\n        while (line_end < buf_end && *line_end != '\\n' && *line_end != '\\r')\n            line_end++;\n        line_end[0] = 0;\n        if (line[0] == ';')\n            continue;\n        if (line[0] == '[' && line_end > line && line_end[-1] == ']')\n        {\n            // Parse \"[Type][Name]\". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code.\n            line_end[-1] = 0;\n            const char* name_end = line_end - 1;\n            const char* type_start = line + 1;\n            char* type_end = (char*)(void*)ImStrchrRange(type_start, name_end, ']');\n            const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL;\n            if (!type_end || !name_start)\n                continue;\n            *type_end = 0; // Overwrite first ']'\n            name_start++;  // Skip second '['\n            entry_handler = FindSettingsHandler(type_start);\n            entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL;\n        }\n        else if (entry_handler != NULL && entry_data != NULL)\n        {\n            // Let type handler parse the line\n            entry_handler->ReadLineFn(&g, entry_handler, entry_data, line);\n        }\n    }\n    g.SettingsLoaded = true;\n\n    // [DEBUG] Restore untouched copy so it can be browsed in Metrics (not strictly necessary)\n    memcpy(buf, ini_data, ini_size);\n\n    // Call post-read handlers\n    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n        if (handler.ApplyAllFn != NULL)\n            handler.ApplyAllFn(&g, &handler);\n}\n\nvoid ImGui::SaveIniSettingsToDisk(const char* ini_filename)\n{\n    ImGuiContext& g = *GImGui;\n    g.SettingsDirtyTimer = 0.0f;\n    if (!ini_filename)\n        return;\n\n    size_t ini_data_size = 0;\n    const char* ini_data = SaveIniSettingsToMemory(&ini_data_size);\n    ImFileHandle f = ImFileOpen(ini_filename, \"wt\");\n    if (!f)\n        return;\n    ImFileWrite(ini_data, sizeof(char), ini_data_size, f);\n    ImFileClose(f);\n}\n\n// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer\nconst char* ImGui::SaveIniSettingsToMemory(size_t* out_size)\n{\n    ImGuiContext& g = *GImGui;\n    g.SettingsDirtyTimer = 0.0f;\n    g.SettingsIniData.Buf.resize(0);\n    g.SettingsIniData.Buf.push_back(0);\n    for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n        handler.WriteAllFn(&g, &handler, &g.SettingsIniData);\n    if (out_size)\n        *out_size = (size_t)g.SettingsIniData.size();\n    return g.SettingsIniData.c_str();\n}\n\nImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)\n{\n    ImGuiContext& g = *GImGui;\n\n    if (g.IO.ConfigDebugIniSettings == false)\n    {\n        // Skip to the \"###\" marker if any. We don't skip past to match the behavior of GetID()\n        // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier.\n        if (const char* p = strstr(name, \"###\"))\n            name = p;\n    }\n    const size_t name_len = strlen(name);\n\n    // Allocate chunk\n    const size_t chunk_size = sizeof(ImGuiWindowSettings) + name_len + 1;\n    ImGuiWindowSettings* settings = g.SettingsWindows.alloc_chunk(chunk_size);\n    IM_PLACEMENT_NEW(settings) ImGuiWindowSettings();\n    settings->ID = ImHashStr(name, name_len);\n    memcpy(settings->GetName(), name, name_len + 1);   // Store with zero terminator\n\n    return settings;\n}\n\n// We don't provide a FindWindowSettingsByName() because Docking system doesn't always hold on names.\n// This is called once per window .ini entry + once per newly instantiated window.\nImGuiWindowSettings* ImGui::FindWindowSettingsByID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n        if (settings->ID == id && !settings->WantDelete)\n            return settings;\n    return NULL;\n}\n\n// This is faster if you are holding on a Window already as we don't need to perform a search.\nImGuiWindowSettings* ImGui::FindWindowSettingsByWindow(ImGuiWindow* window)\n{\n    ImGuiContext& g = *GImGui;\n    if (window->SettingsOffset != -1)\n        return g.SettingsWindows.ptr_from_offset(window->SettingsOffset);\n    return FindWindowSettingsByID(window->ID);\n}\n\n// This will revert window to its initial state, including enabling the ImGuiCond_FirstUseEver/ImGuiCond_Once conditions once more.\nvoid ImGui::ClearWindowSettings(const char* name)\n{\n    //IMGUI_DEBUG_LOG(\"ClearWindowSettings('%s')\\n\", name);\n    ImGuiWindow* window = FindWindowByName(name);\n    if (window != NULL)\n    {\n        window->Flags |= ImGuiWindowFlags_NoSavedSettings;\n        InitOrLoadWindowSettings(window, NULL);\n    }\n    if (ImGuiWindowSettings* settings = window ? FindWindowSettingsByWindow(window) : FindWindowSettingsByID(ImHashStr(name)))\n        settings->WantDelete = true;\n}\n\nstatic void WindowSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)\n{\n    ImGuiContext& g = *ctx;\n    for (ImGuiWindow* window : g.Windows)\n        window->SettingsOffset = -1;\n    g.SettingsWindows.clear();\n}\n\nstatic void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)\n{\n    ImGuiID id = ImHashStr(name);\n    ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByID(id);\n    if (settings)\n        *settings = ImGuiWindowSettings(); // Clear existing if recycling previous entry\n    else\n        settings = ImGui::CreateNewWindowSettings(name);\n    settings->ID = id;\n    settings->WantApply = true;\n    return (void*)settings;\n}\n\nstatic void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)\n{\n    ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;\n    int x, y;\n    int i;\n    if (sscanf(line, \"Pos=%i,%i\", &x, &y) == 2)         { settings->Pos = ImVec2ih((short)x, (short)y); }\n    else if (sscanf(line, \"Size=%i,%i\", &x, &y) == 2)   { settings->Size = ImVec2ih((short)x, (short)y); }\n    else if (sscanf(line, \"Collapsed=%d\", &i) == 1)     { settings->Collapsed = (i != 0); }\n    else if (sscanf(line, \"IsChild=%d\", &i) == 1)       { settings->IsChild = (i != 0); }\n}\n\n// Apply to existing windows (if any)\nstatic void WindowSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)\n{\n    ImGuiContext& g = *ctx;\n    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n        if (settings->WantApply)\n        {\n            if (ImGuiWindow* window = ImGui::FindWindowByID(settings->ID))\n                ApplyWindowSettings(window, settings);\n            settings->WantApply = false;\n        }\n}\n\nstatic void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)\n{\n    // Gather data from windows that were active during this session\n    // (if a window wasn't opened in this session we preserve its settings)\n    ImGuiContext& g = *ctx;\n    for (ImGuiWindow* window : g.Windows)\n    {\n        if (window->Flags & ImGuiWindowFlags_NoSavedSettings)\n            continue;\n\n        ImGuiWindowSettings* settings = ImGui::FindWindowSettingsByWindow(window);\n        if (!settings)\n        {\n            settings = ImGui::CreateNewWindowSettings(window->Name);\n            window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings);\n        }\n        IM_ASSERT(settings->ID == window->ID);\n        settings->Pos = ImVec2ih(window->Pos);\n        settings->Size = ImVec2ih(window->SizeFull);\n        settings->IsChild = (window->Flags & ImGuiWindowFlags_ChildWindow) != 0;\n        settings->Collapsed = window->Collapsed;\n        settings->WantDelete = false;\n    }\n\n    // Write to text buffer\n    buf->reserve(buf->size() + g.SettingsWindows.size() * 6); // ballpark reserve\n    for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n    {\n        if (settings->WantDelete)\n            continue;\n        const char* settings_name = settings->GetName();\n        buf->appendf(\"[%s][%s]\\n\", handler->TypeName, settings_name);\n        if (settings->IsChild)\n        {\n            buf->appendf(\"IsChild=1\\n\");\n            buf->appendf(\"Size=%d,%d\\n\", settings->Size.x, settings->Size.y);\n        }\n        else\n        {\n            buf->appendf(\"Pos=%d,%d\\n\", settings->Pos.x, settings->Pos.y);\n            buf->appendf(\"Size=%d,%d\\n\", settings->Size.x, settings->Size.y);\n            if (settings->Collapsed)\n                buf->appendf(\"Collapsed=1\\n\");\n        }\n        buf->append(\"\\n\");\n    }\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] LOCALIZATION\n//-----------------------------------------------------------------------------\n\nvoid ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count)\n{\n    ImGuiContext& g = *GImGui;\n    for (int n = 0; n < count; n++)\n        g.LocalizationTable[entries[n].Key] = entries[n].Text;\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] VIEWPORTS, PLATFORM WINDOWS\n//-----------------------------------------------------------------------------\n// - GetMainViewport()\n// - SetWindowViewport() [Internal]\n// - UpdateViewportsNewFrame() [Internal]\n// (this section is more complete in the 'docking' branch)\n//-----------------------------------------------------------------------------\n\nImGuiViewport* ImGui::GetMainViewport()\n{\n    ImGuiContext& g = *GImGui;\n    return g.Viewports[0];\n}\n\nvoid ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport)\n{\n    window->Viewport = viewport;\n}\n\n// Update viewports and monitor infos\nstatic void ImGui::UpdateViewportsNewFrame()\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.Viewports.Size == 1);\n\n    // Update main viewport with current platform position.\n    // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent.\n    ImGuiViewportP* main_viewport = g.Viewports[0];\n    main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp;\n    main_viewport->Pos = ImVec2(0.0f, 0.0f);\n    main_viewport->Size = g.IO.DisplaySize;\n\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again.\n        viewport->WorkOffsetMin = viewport->BuildWorkOffsetMin;\n        viewport->WorkOffsetMax = viewport->BuildWorkOffsetMax;\n        viewport->BuildWorkOffsetMin = viewport->BuildWorkOffsetMax = ImVec2(0.0f, 0.0f);\n        viewport->UpdateWorkRect();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DOCKING\n//-----------------------------------------------------------------------------\n\n// (this section is filled in the 'docking' branch)\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] PLATFORM DEPENDENT HELPERS\n//-----------------------------------------------------------------------------\n\n#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS)\n\n#ifdef _MSC_VER\n#pragma comment(lib, \"user32\")\n#pragma comment(lib, \"kernel32\")\n#endif\n\n// Win32 clipboard implementation\n// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown()\nstatic const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx)\n{\n    ImGuiContext& g = *(ImGuiContext*)user_data_ctx;\n    g.ClipboardHandlerData.clear();\n    if (!::OpenClipboard(NULL))\n        return NULL;\n    HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT);\n    if (wbuf_handle == NULL)\n    {\n        ::CloseClipboard();\n        return NULL;\n    }\n    if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle))\n    {\n        int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL);\n        g.ClipboardHandlerData.resize(buf_len);\n        ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL);\n    }\n    ::GlobalUnlock(wbuf_handle);\n    ::CloseClipboard();\n    return g.ClipboardHandlerData.Data;\n}\n\nstatic void SetClipboardTextFn_DefaultImpl(void*, const char* text)\n{\n    if (!::OpenClipboard(NULL))\n        return;\n    const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0);\n    HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR));\n    if (wbuf_handle == NULL)\n    {\n        ::CloseClipboard();\n        return;\n    }\n    WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle);\n    ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length);\n    ::GlobalUnlock(wbuf_handle);\n    ::EmptyClipboard();\n    if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL)\n        ::GlobalFree(wbuf_handle);\n    ::CloseClipboard();\n}\n\n#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS)\n\n#include <Carbon/Carbon.h>  // Use old API to avoid need for separate .mm file\nstatic PasteboardRef main_clipboard = 0;\n\n// OSX clipboard implementation\n// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line!\nstatic void SetClipboardTextFn_DefaultImpl(void*, const char* text)\n{\n    if (!main_clipboard)\n        PasteboardCreate(kPasteboardClipboard, &main_clipboard);\n    PasteboardClear(main_clipboard);\n    CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text));\n    if (cf_data)\n    {\n        PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR(\"public.utf8-plain-text\"), cf_data, 0);\n        CFRelease(cf_data);\n    }\n}\n\nstatic const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx)\n{\n    ImGuiContext& g = *(ImGuiContext*)user_data_ctx;\n    if (!main_clipboard)\n        PasteboardCreate(kPasteboardClipboard, &main_clipboard);\n    PasteboardSynchronize(main_clipboard);\n\n    ItemCount item_count = 0;\n    PasteboardGetItemCount(main_clipboard, &item_count);\n    for (ItemCount i = 0; i < item_count; i++)\n    {\n        PasteboardItemID item_id = 0;\n        PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id);\n        CFArrayRef flavor_type_array = 0;\n        PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array);\n        for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++)\n        {\n            CFDataRef cf_data;\n            if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR(\"public.utf8-plain-text\"), &cf_data) == noErr)\n            {\n                g.ClipboardHandlerData.clear();\n                int length = (int)CFDataGetLength(cf_data);\n                g.ClipboardHandlerData.resize(length + 1);\n                CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data);\n                g.ClipboardHandlerData[length] = 0;\n                CFRelease(cf_data);\n                return g.ClipboardHandlerData.Data;\n            }\n        }\n    }\n    return NULL;\n}\n\n#else\n\n// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers.\nstatic const char* GetClipboardTextFn_DefaultImpl(void* user_data_ctx)\n{\n    ImGuiContext& g = *(ImGuiContext*)user_data_ctx;\n    return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin();\n}\n\nstatic void SetClipboardTextFn_DefaultImpl(void* user_data_ctx, const char* text)\n{\n    ImGuiContext& g = *(ImGuiContext*)user_data_ctx;\n    g.ClipboardHandlerData.clear();\n    const char* text_end = text + strlen(text);\n    g.ClipboardHandlerData.resize((int)(text_end - text) + 1);\n    memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text));\n    g.ClipboardHandlerData[(int)(text_end - text)] = 0;\n}\n\n#endif\n\n// Win32 API IME support (for Asian languages, etc.)\n#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)\n\n#include <imm.h>\n#ifdef _MSC_VER\n#pragma comment(lib, \"imm32\")\n#endif\n\nstatic void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatformImeData* data)\n{\n    // Notify OS Input Method Editor of text input position\n    HWND hwnd = (HWND)viewport->PlatformHandleRaw;\n    if (hwnd == 0)\n        return;\n\n    //::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0);\n    if (HIMC himc = ::ImmGetContext(hwnd))\n    {\n        COMPOSITIONFORM composition_form = {};\n        composition_form.ptCurrentPos.x = (LONG)data->InputPos.x;\n        composition_form.ptCurrentPos.y = (LONG)data->InputPos.y;\n        composition_form.dwStyle = CFS_FORCE_POSITION;\n        ::ImmSetCompositionWindow(himc, &composition_form);\n        CANDIDATEFORM candidate_form = {};\n        candidate_form.dwStyle = CFS_CANDIDATEPOS;\n        candidate_form.ptCurrentPos.x = (LONG)data->InputPos.x;\n        candidate_form.ptCurrentPos.y = (LONG)data->InputPos.y;\n        ::ImmSetCandidateWindow(himc, &candidate_form);\n        ::ImmReleaseContext(hwnd, himc);\n    }\n}\n\n#else\n\nstatic void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport*, ImGuiPlatformImeData*) {}\n\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] METRICS/DEBUGGER WINDOW\n//-----------------------------------------------------------------------------\n// - RenderViewportThumbnail() [Internal]\n// - RenderViewportsThumbnails() [Internal]\n// - DebugTextEncoding()\n// - MetricsHelpMarker() [Internal]\n// - ShowFontAtlas() [Internal]\n// - ShowMetricsWindow()\n// - DebugNodeColumns() [Internal]\n// - DebugNodeDrawList() [Internal]\n// - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal]\n// - DebugNodeFont() [Internal]\n// - DebugNodeFontGlyph() [Internal]\n// - DebugNodeStorage() [Internal]\n// - DebugNodeTabBar() [Internal]\n// - DebugNodeViewport() [Internal]\n// - DebugNodeWindow() [Internal]\n// - DebugNodeWindowSettings() [Internal]\n// - DebugNodeWindowsList() [Internal]\n// - DebugNodeWindowsListByBeginStackParent() [Internal]\n//-----------------------------------------------------------------------------\n\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n\nvoid ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    ImVec2 scale = bb.GetSize() / viewport->Size;\n    ImVec2 off = bb.Min - viewport->Pos * scale;\n    float alpha_mul = 1.0f;\n    window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f));\n    for (ImGuiWindow* thumb_window : g.Windows)\n    {\n        if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow))\n            continue;\n\n        ImRect thumb_r = thumb_window->Rect();\n        ImRect title_r = thumb_window->TitleBarRect();\n        thumb_r = ImRect(ImTrunc(off + thumb_r.Min * scale), ImTrunc(off +  thumb_r.Max * scale));\n        title_r = ImRect(ImTrunc(off + title_r.Min * scale), ImTrunc(off +  ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height\n        thumb_r.ClipWithFull(bb);\n        title_r.ClipWithFull(bb);\n        const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight);\n        window->DrawList->AddRectFilled(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_WindowBg, alpha_mul));\n        window->DrawList->AddRectFilled(title_r.Min, title_r.Max, GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul));\n        window->DrawList->AddRect(thumb_r.Min, thumb_r.Max, GetColorU32(ImGuiCol_Border, alpha_mul));\n        window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r.Min, GetColorU32(ImGuiCol_Text, alpha_mul), thumb_window->Name, FindRenderedTextEnd(thumb_window->Name));\n    }\n    draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul));\n}\n\nstatic void RenderViewportsThumbnails()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports.\n    float SCALE = 1.0f / 8.0f;\n    ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);\n    for (ImGuiViewportP* viewport : g.Viewports)\n        bb_full.Add(viewport->GetMainRect());\n    ImVec2 p = window->DC.CursorPos;\n    ImVec2 off = p - bb_full.Min * SCALE;\n    for (ImGuiViewportP* viewport : g.Viewports)\n    {\n        ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE);\n        ImGui::DebugRenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb);\n    }\n    ImGui::Dummy(bb_full.GetSize() * SCALE);\n}\n\n// Draw an arbitrary US keyboard layout to visualize translated keys\nvoid ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list)\n{\n    const ImVec2 key_size = ImVec2(35.0f, 35.0f);\n    const float  key_rounding = 3.0f;\n    const ImVec2 key_face_size = ImVec2(25.0f, 25.0f);\n    const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f);\n    const float  key_face_rounding = 2.0f;\n    const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f);\n    const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f);\n    const float  key_row_offset = 9.0f;\n\n    ImVec2 board_min = GetCursorScreenPos();\n    ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f);\n    ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y);\n\n    struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; };\n    const KeyLayoutData keys_to_display[] =\n    {\n        { 0, 0, \"\", ImGuiKey_Tab },      { 0, 1, \"Q\", ImGuiKey_Q }, { 0, 2, \"W\", ImGuiKey_W }, { 0, 3, \"E\", ImGuiKey_E }, { 0, 4, \"R\", ImGuiKey_R },\n        { 1, 0, \"\", ImGuiKey_CapsLock }, { 1, 1, \"A\", ImGuiKey_A }, { 1, 2, \"S\", ImGuiKey_S }, { 1, 3, \"D\", ImGuiKey_D }, { 1, 4, \"F\", ImGuiKey_F },\n        { 2, 0, \"\", ImGuiKey_LeftShift },{ 2, 1, \"Z\", ImGuiKey_Z }, { 2, 2, \"X\", ImGuiKey_X }, { 2, 3, \"C\", ImGuiKey_C }, { 2, 4, \"V\", ImGuiKey_V }\n    };\n\n    // Elements rendered manually via ImDrawList API are not clipped automatically.\n    // While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view.\n    Dummy(board_max - board_min);\n    if (!IsItemVisible())\n        return;\n    draw_list->PushClipRect(board_min, board_max, true);\n    for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++)\n    {\n        const KeyLayoutData* key_data = &keys_to_display[n];\n        ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y);\n        ImVec2 key_max = key_min + key_size;\n        draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding);\n        draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding);\n        ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y);\n        ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y);\n        draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f);\n        draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding);\n        ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y);\n        draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label);\n        if (IsKeyDown(key_data->Key))\n            draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding);\n    }\n    draw_list->PopClipRect();\n}\n\n// Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct.\nvoid ImGui::DebugTextEncoding(const char* str)\n{\n    Text(\"Text: \\\"%s\\\"\", str);\n    if (!BeginTable(\"##DebugTextEncoding\", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable))\n        return;\n    TableSetupColumn(\"Offset\");\n    TableSetupColumn(\"UTF-8\");\n    TableSetupColumn(\"Glyph\");\n    TableSetupColumn(\"Codepoint\");\n    TableHeadersRow();\n    for (const char* p = str; *p != 0; )\n    {\n        unsigned int c;\n        const int c_utf8_len = ImTextCharFromUtf8(&c, p, NULL);\n        TableNextColumn();\n        Text(\"%d\", (int)(p - str));\n        TableNextColumn();\n        for (int byte_index = 0; byte_index < c_utf8_len; byte_index++)\n        {\n            if (byte_index > 0)\n                SameLine();\n            Text(\"0x%02X\", (int)(unsigned char)p[byte_index]);\n        }\n        TableNextColumn();\n        if (GetFont()->FindGlyphNoFallback((ImWchar)c))\n            TextUnformatted(p, p + c_utf8_len);\n        else\n            TextUnformatted((c == IM_UNICODE_CODEPOINT_INVALID) ? \"[invalid]\" : \"[missing]\");\n        TableNextColumn();\n        Text(\"U+%04X\", (int)c);\n        p += c_utf8_len;\n    }\n    EndTable();\n}\n\n// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds.\nstatic void MetricsHelpMarker(const char* desc)\n{\n    ImGui::TextDisabled(\"(?)\");\n    if (ImGui::BeginItemTooltip())\n    {\n        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);\n        ImGui::TextUnformatted(desc);\n        ImGui::PopTextWrapPos();\n        ImGui::EndTooltip();\n    }\n}\n\n// [DEBUG] List fonts in a font atlas and display its texture\nvoid ImGui::ShowFontAtlas(ImFontAtlas* atlas)\n{\n    for (ImFont* font : atlas->Fonts)\n    {\n        PushID(font);\n        DebugNodeFont(font);\n        PopID();\n    }\n    if (TreeNode(\"Font Atlas\", \"Font Atlas (%dx%d pixels)\", atlas->TexWidth, atlas->TexHeight))\n    {\n        ImGuiContext& g = *GImGui;\n        ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;\n        Checkbox(\"Tint with Text Color\", &cfg->ShowAtlasTintedWithTextColor); // Using text color ensure visibility of core atlas data, but will alter custom colored icons\n        ImVec4 tint_col = cfg->ShowAtlasTintedWithTextColor ? GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);\n        ImVec4 border_col = GetStyleColorVec4(ImGuiCol_Border);\n        Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), tint_col, border_col);\n        TreePop();\n    }\n}\n\nvoid ImGui::ShowMetricsWindow(bool* p_open)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;\n    if (cfg->ShowDebugLog)\n        ShowDebugLogWindow(&cfg->ShowDebugLog);\n    if (cfg->ShowIDStackTool)\n        ShowIDStackToolWindow(&cfg->ShowIDStackTool);\n\n    if (!Begin(\"Dear ImGui Metrics/Debugger\", p_open) || GetCurrentWindow()->BeginCount > 1)\n    {\n        End();\n        return;\n    }\n\n    // Basic info\n    Text(\"Dear ImGui %s\", GetVersion());\n    Text(\"Application average %.3f ms/frame (%.1f FPS)\", 1000.0f / io.Framerate, io.Framerate);\n    Text(\"%d vertices, %d indices (%d triangles)\", io.MetricsRenderVertices, io.MetricsRenderIndices, io.MetricsRenderIndices / 3);\n    Text(\"%d visible windows, %d current allocations\", io.MetricsRenderWindows, g.DebugAllocInfo.TotalAllocCount - g.DebugAllocInfo.TotalFreeCount);\n    //SameLine(); if (SmallButton(\"GC\")) { g.GcCompactAll = true; }\n\n    Separator();\n\n    // Debugging enums\n    enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentIdeal, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type\n    const char* wrt_rects_names[WRT_Count] = { \"OuterRect\", \"OuterRectClipped\", \"InnerRect\", \"InnerClipRect\", \"WorkRect\", \"Content\", \"ContentIdeal\", \"ContentRegionRect\" };\n    enum { TRT_OuterRect, TRT_InnerRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsWorkRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentFrozen, TRT_ColumnsContentUnfrozen, TRT_Count }; // Tables Rect Type\n    const char* trt_rects_names[TRT_Count] = { \"OuterRect\", \"InnerRect\", \"WorkRect\", \"HostClipRect\", \"InnerClipRect\", \"BackgroundClipRect\", \"ColumnsRect\", \"ColumnsWorkRect\", \"ColumnsClipRect\", \"ColumnsContentHeadersUsed\", \"ColumnsContentHeadersIdeal\", \"ColumnsContentFrozen\", \"ColumnsContentUnfrozen\" };\n    if (cfg->ShowWindowsRectsType < 0)\n        cfg->ShowWindowsRectsType = WRT_WorkRect;\n    if (cfg->ShowTablesRectsType < 0)\n        cfg->ShowTablesRectsType = TRT_WorkRect;\n\n    struct Funcs\n    {\n        static ImRect GetTableRect(ImGuiTable* table, int rect_type, int n)\n        {\n            ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); // Always using last submitted instance\n            if (rect_type == TRT_OuterRect)                     { return table->OuterRect; }\n            else if (rect_type == TRT_InnerRect)                { return table->InnerRect; }\n            else if (rect_type == TRT_WorkRect)                 { return table->WorkRect; }\n            else if (rect_type == TRT_HostClipRect)             { return table->HostClipRect; }\n            else if (rect_type == TRT_InnerClipRect)            { return table->InnerClipRect; }\n            else if (rect_type == TRT_BackgroundClipRect)       { return table->BgClipRect; }\n            else if (rect_type == TRT_ColumnsRect)              { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->MinX, table->InnerClipRect.Min.y, c->MaxX, table->InnerClipRect.Min.y + table_instance->LastOuterHeight); }\n            else if (rect_type == TRT_ColumnsWorkRect)          { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->WorkRect.Min.y, c->WorkMaxX, table->WorkRect.Max.y); }\n            else if (rect_type == TRT_ColumnsClipRect)          { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; }\n            else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); } // Note: y1/y2 not always accurate\n            else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastTopHeadersRowHeight); }\n            else if (rect_type == TRT_ColumnsContentFrozen)     { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); }\n            else if (rect_type == TRT_ColumnsContentUnfrozen)   { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); }\n            IM_ASSERT(0);\n            return ImRect();\n        }\n\n        static ImRect GetWindowRect(ImGuiWindow* window, int rect_type)\n        {\n            if (rect_type == WRT_OuterRect)                 { return window->Rect(); }\n            else if (rect_type == WRT_OuterRectClipped)     { return window->OuterRectClipped; }\n            else if (rect_type == WRT_InnerRect)            { return window->InnerRect; }\n            else if (rect_type == WRT_InnerClipRect)        { return window->InnerClipRect; }\n            else if (rect_type == WRT_WorkRect)             { return window->WorkRect; }\n            else if (rect_type == WRT_Content)              { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }\n            else if (rect_type == WRT_ContentIdeal)         { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }\n            else if (rect_type == WRT_ContentRegionRect)    { return window->ContentRegionRect; }\n            IM_ASSERT(0);\n            return ImRect();\n        }\n    };\n\n    // Tools\n    if (TreeNode(\"Tools\"))\n    {\n        bool show_encoding_viewer = TreeNode(\"UTF-8 Encoding viewer\");\n        SameLine();\n        MetricsHelpMarker(\"You can also call ImGui::DebugTextEncoding() from your code with a given string to test that your UTF-8 encoding settings are correct.\");\n        if (show_encoding_viewer)\n        {\n            static char buf[100] = \"\";\n            SetNextItemWidth(-FLT_MIN);\n            InputText(\"##Text\", buf, IM_ARRAYSIZE(buf));\n            if (buf[0] != 0)\n                DebugTextEncoding(buf);\n            TreePop();\n        }\n\n        // The Item Picker tool is super useful to visually select an item and break into the call-stack of where it was submitted.\n        if (Checkbox(\"Show Item Picker\", &g.DebugItemPickerActive) && g.DebugItemPickerActive)\n            DebugStartItemPicker();\n        SameLine();\n        MetricsHelpMarker(\"Will call the IM_DEBUG_BREAK() macro to break in debugger.\\nWarning: If you don't have a debugger attached, this will probably crash.\");\n\n        Checkbox(\"Show Debug Log\", &cfg->ShowDebugLog);\n        SameLine();\n        MetricsHelpMarker(\"You can also call ImGui::ShowDebugLogWindow() from your code.\");\n\n        Checkbox(\"Show ID Stack Tool\", &cfg->ShowIDStackTool);\n        SameLine();\n        MetricsHelpMarker(\"You can also call ImGui::ShowIDStackToolWindow() from your code.\");\n\n        Checkbox(\"Show windows begin order\", &cfg->ShowWindowsBeginOrder);\n        Checkbox(\"Show windows rectangles\", &cfg->ShowWindowsRects);\n        SameLine();\n        SetNextItemWidth(GetFontSize() * 12);\n        cfg->ShowWindowsRects |= Combo(\"##show_windows_rect_type\", &cfg->ShowWindowsRectsType, wrt_rects_names, WRT_Count, WRT_Count);\n        if (cfg->ShowWindowsRects && g.NavWindow != NULL)\n        {\n            BulletText(\"'%s':\", g.NavWindow->Name);\n            Indent();\n            for (int rect_n = 0; rect_n < WRT_Count; rect_n++)\n            {\n                ImRect r = Funcs::GetWindowRect(g.NavWindow, rect_n);\n                Text(\"(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s\", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), wrt_rects_names[rect_n]);\n            }\n            Unindent();\n        }\n\n        Checkbox(\"Show tables rectangles\", &cfg->ShowTablesRects);\n        SameLine();\n        SetNextItemWidth(GetFontSize() * 12);\n        cfg->ShowTablesRects |= Combo(\"##show_table_rects_type\", &cfg->ShowTablesRectsType, trt_rects_names, TRT_Count, TRT_Count);\n        if (cfg->ShowTablesRects && g.NavWindow != NULL)\n        {\n            for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)\n            {\n                ImGuiTable* table = g.Tables.TryGetMapData(table_n);\n                if (table == NULL || table->LastFrameActive < g.FrameCount - 1 || (table->OuterWindow != g.NavWindow && table->InnerWindow != g.NavWindow))\n                    continue;\n\n                BulletText(\"Table 0x%08X (%d columns, in '%s')\", table->ID, table->ColumnsCount, table->OuterWindow->Name);\n                if (IsItemHovered())\n                    GetForegroundDrawList()->AddRect(table->OuterRect.Min - ImVec2(1, 1), table->OuterRect.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);\n                Indent();\n                char buf[128];\n                for (int rect_n = 0; rect_n < TRT_Count; rect_n++)\n                {\n                    if (rect_n >= TRT_ColumnsRect)\n                    {\n                        if (rect_n != TRT_ColumnsRect && rect_n != TRT_ColumnsClipRect)\n                            continue;\n                        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n                        {\n                            ImRect r = Funcs::GetTableRect(table, rect_n, column_n);\n                            ImFormatString(buf, IM_ARRAYSIZE(buf), \"(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) Col %d %s\", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), column_n, trt_rects_names[rect_n]);\n                            Selectable(buf);\n                            if (IsItemHovered())\n                                GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);\n                        }\n                    }\n                    else\n                    {\n                        ImRect r = Funcs::GetTableRect(table, rect_n, -1);\n                        ImFormatString(buf, IM_ARRAYSIZE(buf), \"(%6.1f,%6.1f) (%6.1f,%6.1f) Size (%6.1f,%6.1f) %s\", r.Min.x, r.Min.y, r.Max.x, r.Max.y, r.GetWidth(), r.GetHeight(), trt_rects_names[rect_n]);\n                        Selectable(buf);\n                        if (IsItemHovered())\n                            GetForegroundDrawList()->AddRect(r.Min - ImVec2(1, 1), r.Max + ImVec2(1, 1), IM_COL32(255, 255, 0, 255), 0.0f, 0, 2.0f);\n                    }\n                }\n                Unindent();\n            }\n        }\n        Checkbox(\"Show groups rectangles\", &g.DebugShowGroupRects); // Storing in context as this is used by group code and prefers to be in hot-data\n\n        Checkbox(\"Debug Begin/BeginChild return value\", &io.ConfigDebugBeginReturnValueLoop);\n        SameLine();\n        MetricsHelpMarker(\"Some calls to Begin()/BeginChild() will return false.\\n\\nWill cycle through window depths then repeat. Windows should be flickering while running.\");\n\n        TreePop();\n    }\n\n    // Windows\n    if (TreeNode(\"Windows\", \"Windows (%d)\", g.Windows.Size))\n    {\n        //SetNextItemOpen(true, ImGuiCond_Once);\n        DebugNodeWindowsList(&g.Windows, \"By display order\");\n        DebugNodeWindowsList(&g.WindowsFocusOrder, \"By focus order (root windows)\");\n        if (TreeNode(\"By submission order (begin stack)\"))\n        {\n            // Here we display windows in their submitted order/hierarchy, however note that the Begin stack doesn't constitute a Parent<>Child relationship!\n            ImVector<ImGuiWindow*>& temp_buffer = g.WindowsTempSortBuffer;\n            temp_buffer.resize(0);\n            for (ImGuiWindow* window : g.Windows)\n                if (window->LastFrameActive + 1 >= g.FrameCount)\n                    temp_buffer.push_back(window);\n            struct Func { static int IMGUI_CDECL WindowComparerByBeginOrder(const void* lhs, const void* rhs) { return ((int)(*(const ImGuiWindow* const *)lhs)->BeginOrderWithinContext - (*(const ImGuiWindow* const*)rhs)->BeginOrderWithinContext); } };\n            ImQsort(temp_buffer.Data, (size_t)temp_buffer.Size, sizeof(ImGuiWindow*), Func::WindowComparerByBeginOrder);\n            DebugNodeWindowsListByBeginStackParent(temp_buffer.Data, temp_buffer.Size, NULL);\n            TreePop();\n        }\n\n        TreePop();\n    }\n\n    // DrawLists\n    int drawlist_count = 0;\n    for (ImGuiViewportP* viewport : g.Viewports)\n        drawlist_count += viewport->DrawDataP.CmdLists.Size;\n    if (TreeNode(\"DrawLists\", \"DrawLists (%d)\", drawlist_count))\n    {\n        Checkbox(\"Show ImDrawCmd mesh when hovering\", &cfg->ShowDrawCmdMesh);\n        Checkbox(\"Show ImDrawCmd bounding boxes when hovering\", &cfg->ShowDrawCmdBoundingBoxes);\n        for (ImGuiViewportP* viewport : g.Viewports)\n            for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)\n                DebugNodeDrawList(NULL, viewport, draw_list, \"DrawList\");\n        TreePop();\n    }\n\n    // Viewports\n    if (TreeNode(\"Viewports\", \"Viewports (%d)\", g.Viewports.Size))\n    {\n        Indent(GetTreeNodeToLabelSpacing());\n        RenderViewportsThumbnails();\n        Unindent(GetTreeNodeToLabelSpacing());\n        for (ImGuiViewportP* viewport : g.Viewports)\n            DebugNodeViewport(viewport);\n        TreePop();\n    }\n\n    // Details for Popups\n    if (TreeNode(\"Popups\", \"Popups (%d)\", g.OpenPopupStack.Size))\n    {\n        for (const ImGuiPopupData& popup_data : g.OpenPopupStack)\n        {\n            // As it's difficult to interact with tree nodes while popups are open, we display everything inline.\n            ImGuiWindow* window = popup_data.Window;\n            BulletText(\"PopupID: %08x, Window: '%s' (%s%s), BackupNavWindow '%s', ParentWindow '%s'\",\n                popup_data.PopupId, window ? window->Name : \"NULL\", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? \"Child;\" : \"\", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? \"Menu;\" : \"\",\n                popup_data.BackupNavWindow ? popup_data.BackupNavWindow->Name : \"NULL\", window && window->ParentWindow ? window->ParentWindow->Name : \"NULL\");\n        }\n        TreePop();\n    }\n\n    // Details for TabBars\n    if (TreeNode(\"TabBars\", \"Tab Bars (%d)\", g.TabBars.GetAliveCount()))\n    {\n        for (int n = 0; n < g.TabBars.GetMapSize(); n++)\n            if (ImGuiTabBar* tab_bar = g.TabBars.TryGetMapData(n))\n            {\n                PushID(tab_bar);\n                DebugNodeTabBar(tab_bar, \"TabBar\");\n                PopID();\n            }\n        TreePop();\n    }\n\n    // Details for Tables\n    if (TreeNode(\"Tables\", \"Tables (%d)\", g.Tables.GetAliveCount()))\n    {\n        for (int n = 0; n < g.Tables.GetMapSize(); n++)\n            if (ImGuiTable* table = g.Tables.TryGetMapData(n))\n                DebugNodeTable(table);\n        TreePop();\n    }\n\n    // Details for Fonts\n    ImFontAtlas* atlas = g.IO.Fonts;\n    if (TreeNode(\"Fonts\", \"Fonts (%d)\", atlas->Fonts.Size))\n    {\n        ShowFontAtlas(atlas);\n        TreePop();\n    }\n\n    // Details for InputText\n    if (TreeNode(\"InputText\"))\n    {\n        DebugNodeInputTextState(&g.InputTextState);\n        TreePop();\n    }\n\n    // Details for TypingSelect\n    if (TreeNode(\"TypingSelect\", \"TypingSelect (%d)\", g.TypingSelectState.SearchBuffer[0] != 0 ? 1 : 0))\n    {\n        DebugNodeTypingSelectState(&g.TypingSelectState);\n        TreePop();\n    }\n\n    // Details for Docking\n#ifdef IMGUI_HAS_DOCK\n    if (TreeNode(\"Docking\"))\n    {\n        TreePop();\n    }\n#endif // #ifdef IMGUI_HAS_DOCK\n\n    // Settings\n    if (TreeNode(\"Settings\"))\n    {\n        if (SmallButton(\"Clear\"))\n            ClearIniSettings();\n        SameLine();\n        if (SmallButton(\"Save to memory\"))\n            SaveIniSettingsToMemory();\n        SameLine();\n        if (SmallButton(\"Save to disk\"))\n            SaveIniSettingsToDisk(g.IO.IniFilename);\n        SameLine();\n        if (g.IO.IniFilename)\n            Text(\"\\\"%s\\\"\", g.IO.IniFilename);\n        else\n            TextUnformatted(\"<NULL>\");\n        Checkbox(\"io.ConfigDebugIniSettings\", &io.ConfigDebugIniSettings);\n        Text(\"SettingsDirtyTimer %.2f\", g.SettingsDirtyTimer);\n        if (TreeNode(\"SettingsHandlers\", \"Settings handlers: (%d)\", g.SettingsHandlers.Size))\n        {\n            for (ImGuiSettingsHandler& handler : g.SettingsHandlers)\n                BulletText(\"\\\"%s\\\"\", handler.TypeName);\n            TreePop();\n        }\n        if (TreeNode(\"SettingsWindows\", \"Settings packed data: Windows: %d bytes\", g.SettingsWindows.size()))\n        {\n            for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings))\n                DebugNodeWindowSettings(settings);\n            TreePop();\n        }\n\n        if (TreeNode(\"SettingsTables\", \"Settings packed data: Tables: %d bytes\", g.SettingsTables.size()))\n        {\n            for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))\n                DebugNodeTableSettings(settings);\n            TreePop();\n        }\n\n#ifdef IMGUI_HAS_DOCK\n#endif // #ifdef IMGUI_HAS_DOCK\n\n        if (TreeNode(\"SettingsIniData\", \"Settings unpacked data (.ini): %d bytes\", g.SettingsIniData.size()))\n        {\n            InputTextMultiline(\"##Ini\", (char*)(void*)g.SettingsIniData.c_str(), g.SettingsIniData.Buf.Size, ImVec2(-FLT_MIN, GetTextLineHeight() * 20), ImGuiInputTextFlags_ReadOnly);\n            TreePop();\n        }\n        TreePop();\n    }\n\n    // Settings\n    if (TreeNode(\"Memory allocations\"))\n    {\n        ImGuiDebugAllocInfo* info = &g.DebugAllocInfo;\n        Text(\"%d current allocations\", info->TotalAllocCount - info->TotalFreeCount);\n        Text(\"Recent frames with allocations:\");\n        int buf_size = IM_ARRAYSIZE(info->LastEntriesBuf);\n        for (int n = buf_size - 1; n >= 0; n--)\n        {\n            ImGuiDebugAllocEntry* entry = &info->LastEntriesBuf[(info->LastEntriesIdx - n + buf_size) % buf_size];\n            BulletText(\"Frame %06d: %+3d ( %2d malloc, %2d free )%s\", entry->FrameCount, entry->AllocCount - entry->FreeCount, entry->AllocCount, entry->FreeCount, (n == 0) ? \" (most recent)\" : \"\");\n        }\n        TreePop();\n    }\n\n    if (TreeNode(\"Inputs\"))\n    {\n        Text(\"KEYBOARD/GAMEPAD/MOUSE KEYS\");\n        {\n            // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allows displaying the data for old/new backends.\n            // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.\n            Indent();\n#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO\n            struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };\n#else\n            struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array\n            //Text(\"Legacy raw:\");      for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { SameLine(); Text(\"\\\"%s\\\" %d\", GetKeyName(key), key); } }\n#endif\n            Text(\"Keys down:\");         for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyDown(key)) continue;     SameLine(); Text(IsNamedKey(key) ? \"\\\"%s\\\"\" : \"\\\"%s\\\" %d\", GetKeyName(key), key); SameLine(); Text(\"(%.02f)\", GetKeyData(key)->DownDuration); }\n            Text(\"Keys pressed:\");      for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyPressed(key)) continue;  SameLine(); Text(IsNamedKey(key) ? \"\\\"%s\\\"\" : \"\\\"%s\\\" %d\", GetKeyName(key), key); }\n            Text(\"Keys released:\");     for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? \"\\\"%s\\\"\" : \"\\\"%s\\\" %d\", GetKeyName(key), key); }\n            Text(\"Keys mods: %s%s%s%s\", io.KeyCtrl ? \"CTRL \" : \"\", io.KeyShift ? \"SHIFT \" : \"\", io.KeyAlt ? \"ALT \" : \"\", io.KeySuper ? \"SUPER \" : \"\");\n            Text(\"Chars queue:\");       for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text(\"\\'%c\\' (0x%04X)\", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.\n            DebugRenderKeyboardPreview(GetWindowDrawList());\n            Unindent();\n        }\n\n        Text(\"MOUSE STATE\");\n        {\n            Indent();\n            if (IsMousePosValid())\n                Text(\"Mouse pos: (%g, %g)\", io.MousePos.x, io.MousePos.y);\n            else\n                Text(\"Mouse pos: <INVALID>\");\n            Text(\"Mouse delta: (%g, %g)\", io.MouseDelta.x, io.MouseDelta.y);\n            int count = IM_ARRAYSIZE(io.MouseDown);\n            Text(\"Mouse down:\");     for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text(\"b%d (%.02f secs)\", i, io.MouseDownDuration[i]); }\n            Text(\"Mouse clicked:\");  for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text(\"b%d (%d)\", i, io.MouseClickedCount[i]); }\n            Text(\"Mouse released:\"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text(\"b%d\", i); }\n            Text(\"Mouse wheel: %.1f\", io.MouseWheel);\n            Text(\"MouseStationaryTimer: %.2f\", g.MouseStationaryTimer);\n            Text(\"Mouse source: %s\", GetMouseSourceName(io.MouseSource));\n            Text(\"Pen Pressure: %.1f\", io.PenPressure); // Note: currently unused\n            Unindent();\n        }\n\n        Text(\"MOUSE WHEELING\");\n        {\n            Indent();\n            Text(\"WheelingWindow: '%s'\", g.WheelingWindow ? g.WheelingWindow->Name : \"NULL\");\n            Text(\"WheelingWindowReleaseTimer: %.2f\", g.WheelingWindowReleaseTimer);\n            Text(\"WheelingAxisAvg[] = { %.3f, %.3f }, Main Axis: %s\", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y, (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? \"X\" : (g.WheelingAxisAvg.x < g.WheelingAxisAvg.y) ? \"Y\" : \"<none>\");\n            Unindent();\n        }\n\n        Text(\"KEY OWNERS\");\n        {\n            Indent();\n            if (BeginChild(\"##owners\", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 6), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))\n                for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))\n                {\n                    ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(&g, key);\n                    if (owner_data->OwnerCurr == ImGuiKeyOwner_None)\n                        continue;\n                    Text(\"%s: 0x%08X%s\", GetKeyName(key), owner_data->OwnerCurr,\n                        owner_data->LockUntilRelease ? \" LockUntilRelease\" : owner_data->LockThisFrame ? \" LockThisFrame\" : \"\");\n                    DebugLocateItemOnHover(owner_data->OwnerCurr);\n                }\n            EndChild();\n            Unindent();\n        }\n        Text(\"SHORTCUT ROUTING\");\n        {\n            Indent();\n            if (BeginChild(\"##routes\", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 6), ImGuiChildFlags_FrameStyle | ImGuiChildFlags_ResizeY, ImGuiWindowFlags_NoSavedSettings))\n                for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1))\n                {\n                    ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable;\n                    for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; )\n                    {\n                        char key_chord_name[64];\n                        ImGuiKeyRoutingData* routing_data = &rt->Entries[idx];\n                        GetKeyChordName(key | routing_data->Mods, key_chord_name, IM_ARRAYSIZE(key_chord_name));\n                        Text(\"%s: 0x%08X\", key_chord_name, routing_data->RoutingCurr);\n                        DebugLocateItemOnHover(routing_data->RoutingCurr);\n                        idx = routing_data->NextEntryIndex;\n                    }\n                }\n            EndChild();\n            Text(\"(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)\", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);\n            Unindent();\n        }\n        TreePop();\n    }\n\n    if (TreeNode(\"Internal state\"))\n    {\n        Text(\"WINDOWING\");\n        Indent();\n        Text(\"HoveredWindow: '%s'\", g.HoveredWindow ? g.HoveredWindow->Name : \"NULL\");\n        Text(\"HoveredWindow->Root: '%s'\", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : \"NULL\");\n        Text(\"HoveredWindowUnderMovingWindow: '%s'\", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : \"NULL\");\n        Text(\"MovingWindow: '%s'\", g.MovingWindow ? g.MovingWindow->Name : \"NULL\");\n        Unindent();\n\n        Text(\"ITEMS\");\n        Indent();\n        Text(\"ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s\", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource));\n        DebugLocateItemOnHover(g.ActiveId);\n        Text(\"ActiveIdWindow: '%s'\", g.ActiveIdWindow ? g.ActiveIdWindow->Name : \"NULL\");\n        Text(\"ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X\", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);\n        Text(\"HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d\", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame\n        Text(\"HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f\", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer);\n        Text(\"DragDrop: %d, SourceId = 0x%08X, Payload \\\"%s\\\" (%d bytes)\", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);\n        DebugLocateItemOnHover(g.DragDropPayload.SourceId);\n        Unindent();\n\n        Text(\"NAV,FOCUS\");\n        Indent();\n        Text(\"NavWindow: '%s'\", g.NavWindow ? g.NavWindow->Name : \"NULL\");\n        Text(\"NavId: 0x%08X, NavLayer: %d\", g.NavId, g.NavLayer);\n        DebugLocateItemOnHover(g.NavId);\n        Text(\"NavInputSource: %s\", GetInputSourceName(g.NavInputSource));\n        Text(\"NavLastValidSelectionUserData = %\" IM_PRId64 \" (0x%\" IM_PRIX64 \")\", g.NavLastValidSelectionUserData, g.NavLastValidSelectionUserData);\n        Text(\"NavActive: %d, NavVisible: %d\", g.IO.NavActive, g.IO.NavVisible);\n        Text(\"NavActivateId/DownId/PressedId: %08X/%08X/%08X\", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId);\n        Text(\"NavActivateFlags: %04X\", g.NavActivateFlags);\n        Text(\"NavDisableHighlight: %d, NavDisableMouseHover: %d\", g.NavDisableHighlight, g.NavDisableMouseHover);\n        Text(\"NavFocusScopeId = 0x%08X\", g.NavFocusScopeId);\n        Text(\"NavWindowingTarget: '%s'\", g.NavWindowingTarget ? g.NavWindowingTarget->Name : \"NULL\");\n        Unindent();\n\n        TreePop();\n    }\n\n    // Overlay: Display windows Rectangles and Begin Order\n    if (cfg->ShowWindowsRects || cfg->ShowWindowsBeginOrder)\n    {\n        for (ImGuiWindow* window : g.Windows)\n        {\n            if (!window->WasActive)\n                continue;\n            ImDrawList* draw_list = GetForegroundDrawList(window);\n            if (cfg->ShowWindowsRects)\n            {\n                ImRect r = Funcs::GetWindowRect(window, cfg->ShowWindowsRectsType);\n                draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));\n            }\n            if (cfg->ShowWindowsBeginOrder && !(window->Flags & ImGuiWindowFlags_ChildWindow))\n            {\n                char buf[32];\n                ImFormatString(buf, IM_ARRAYSIZE(buf), \"%d\", window->BeginOrderWithinContext);\n                float font_size = GetFontSize();\n                draw_list->AddRectFilled(window->Pos, window->Pos + ImVec2(font_size, font_size), IM_COL32(200, 100, 100, 255));\n                draw_list->AddText(window->Pos, IM_COL32(255, 255, 255, 255), buf);\n            }\n        }\n    }\n\n    // Overlay: Display Tables Rectangles\n    if (cfg->ShowTablesRects)\n    {\n        for (int table_n = 0; table_n < g.Tables.GetMapSize(); table_n++)\n        {\n            ImGuiTable* table = g.Tables.TryGetMapData(table_n);\n            if (table == NULL || table->LastFrameActive < g.FrameCount - 1)\n                continue;\n            ImDrawList* draw_list = GetForegroundDrawList(table->OuterWindow);\n            if (cfg->ShowTablesRectsType >= TRT_ColumnsRect)\n            {\n                for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n                {\n                    ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, column_n);\n                    ImU32 col = (table->HoveredColumnBody == column_n) ? IM_COL32(255, 255, 128, 255) : IM_COL32(255, 0, 128, 255);\n                    float thickness = (table->HoveredColumnBody == column_n) ? 3.0f : 1.0f;\n                    draw_list->AddRect(r.Min, r.Max, col, 0.0f, 0, thickness);\n                }\n            }\n            else\n            {\n                ImRect r = Funcs::GetTableRect(table, cfg->ShowTablesRectsType, -1);\n                draw_list->AddRect(r.Min, r.Max, IM_COL32(255, 0, 128, 255));\n            }\n        }\n    }\n\n#ifdef IMGUI_HAS_DOCK\n    // Overlay: Display Docking info\n    if (show_docking_nodes && g.IO.KeyCtrl)\n    {\n    }\n#endif // #ifdef IMGUI_HAS_DOCK\n\n    End();\n}\n\n// [DEBUG] Display contents of Columns\nvoid ImGui::DebugNodeColumns(ImGuiOldColumns* columns)\n{\n    if (!TreeNode((void*)(uintptr_t)columns->ID, \"Columns Id: 0x%08X, Count: %d, Flags: 0x%04X\", columns->ID, columns->Count, columns->Flags))\n        return;\n    BulletText(\"Width: %.1f (MinX: %.1f, MaxX: %.1f)\", columns->OffMaxX - columns->OffMinX, columns->OffMinX, columns->OffMaxX);\n    for (ImGuiOldColumnData& column : columns->Columns)\n        BulletText(\"Column %02d: OffsetNorm %.3f (= %.1f px)\", (int)columns->Columns.index_from_ptr(&column), column.OffsetNorm, GetColumnOffsetFromNorm(columns, column.OffsetNorm));\n    TreePop();\n}\n\n// [DEBUG] Display contents of ImDrawList\nvoid ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label)\n{\n    ImGuiContext& g = *GImGui;\n    IM_UNUSED(viewport); // Used in docking branch\n    ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig;\n    int cmd_count = draw_list->CmdBuffer.Size;\n    if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL)\n        cmd_count--;\n    bool node_open = TreeNode(draw_list, \"%s: '%s' %d vtx, %d indices, %d cmds\", label, draw_list->_OwnerName ? draw_list->_OwnerName : \"\", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, cmd_count);\n    if (draw_list == GetWindowDrawList())\n    {\n        SameLine();\n        TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), \"CURRENTLY APPENDING\"); // Can't display stats for active draw list! (we don't have the data double-buffered)\n        if (node_open)\n            TreePop();\n        return;\n    }\n\n    ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list\n    if (window && IsItemHovered() && fg_draw_list)\n        fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));\n    if (!node_open)\n        return;\n\n    if (window && !window->WasActive)\n        TextDisabled(\"Warning: owning Window is inactive. This DrawList is not being rendered!\");\n\n    for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.Data; pcmd < draw_list->CmdBuffer.Data + cmd_count; pcmd++)\n    {\n        if (pcmd->UserCallback)\n        {\n            BulletText(\"Callback %p, user_data %p\", pcmd->UserCallback, pcmd->UserCallbackData);\n            continue;\n        }\n\n        char buf[300];\n        ImFormatString(buf, IM_ARRAYSIZE(buf), \"DrawCmd:%5d tris, Tex 0x%p, ClipRect (%4.0f,%4.0f)-(%4.0f,%4.0f)\",\n            pcmd->ElemCount / 3, (void*)(intptr_t)pcmd->TextureId,\n            pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w);\n        bool pcmd_node_open = TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), \"%s\", buf);\n        if (IsItemHovered() && (cfg->ShowDrawCmdMesh || cfg->ShowDrawCmdBoundingBoxes) && fg_draw_list)\n            DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, cfg->ShowDrawCmdMesh, cfg->ShowDrawCmdBoundingBoxes);\n        if (!pcmd_node_open)\n            continue;\n\n        // Calculate approximate coverage area (touched pixel count)\n        // This will be in pixels squared as long there's no post-scaling happening to the renderer output.\n        const ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL;\n        const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + pcmd->VtxOffset;\n        float total_area = 0.0f;\n        for (unsigned int idx_n = pcmd->IdxOffset; idx_n < pcmd->IdxOffset + pcmd->ElemCount; )\n        {\n            ImVec2 triangle[3];\n            for (int n = 0; n < 3; n++, idx_n++)\n                triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos;\n            total_area += ImTriangleArea(triangle[0], triangle[1], triangle[2]);\n        }\n\n        // Display vertex information summary. Hover to get all triangles drawn in wire-frame\n        ImFormatString(buf, IM_ARRAYSIZE(buf), \"Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px\", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area);\n        Selectable(buf);\n        if (IsItemHovered() && fg_draw_list)\n            DebugNodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, true, false);\n\n        // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.\n        ImGuiListClipper clipper;\n        clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.\n        while (clipper.Step())\n            for (int prim = clipper.DisplayStart, idx_i = pcmd->IdxOffset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)\n            {\n                char* buf_p = buf, * buf_end = buf + IM_ARRAYSIZE(buf);\n                ImVec2 triangle[3];\n                for (int n = 0; n < 3; n++, idx_i++)\n                {\n                    const ImDrawVert& v = vtx_buffer[idx_buffer ? idx_buffer[idx_i] : idx_i];\n                    triangle[n] = v.pos;\n                    buf_p += ImFormatString(buf_p, buf_end - buf_p, \"%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\\n\",\n                        (n == 0) ? \"Vert:\" : \"     \", idx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col);\n                }\n\n                Selectable(buf, false);\n                if (fg_draw_list && IsItemHovered())\n                {\n                    ImDrawListFlags backup_flags = fg_draw_list->Flags;\n                    fg_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.\n                    fg_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f);\n                    fg_draw_list->Flags = backup_flags;\n                }\n            }\n        TreePop();\n    }\n    TreePop();\n}\n\n// [DEBUG] Display mesh/aabb of a ImDrawCmd\nvoid ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb)\n{\n    IM_ASSERT(show_mesh || show_aabb);\n\n    // Draw wire-frame version of all triangles\n    ImRect clip_rect = draw_cmd->ClipRect;\n    ImRect vtxs_rect(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);\n    ImDrawListFlags backup_flags = out_draw_list->Flags;\n    out_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines is more readable for very large and thin triangles.\n    for (unsigned int idx_n = draw_cmd->IdxOffset, idx_end = draw_cmd->IdxOffset + draw_cmd->ElemCount; idx_n < idx_end; )\n    {\n        ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // We don't hold on those pointers past iterations as ->AddPolyline() may invalidate them if out_draw_list==draw_list\n        ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data + draw_cmd->VtxOffset;\n\n        ImVec2 triangle[3];\n        for (int n = 0; n < 3; n++, idx_n++)\n            vtxs_rect.Add((triangle[n] = vtx_buffer[idx_buffer ? idx_buffer[idx_n] : idx_n].pos));\n        if (show_mesh)\n            out_draw_list->AddPolyline(triangle, 3, IM_COL32(255, 255, 0, 255), ImDrawFlags_Closed, 1.0f); // In yellow: mesh triangles\n    }\n    // Draw bounding boxes\n    if (show_aabb)\n    {\n        out_draw_list->AddRect(ImTrunc(clip_rect.Min), ImTrunc(clip_rect.Max), IM_COL32(255, 0, 255, 255)); // In pink: clipping rectangle submitted to GPU\n        out_draw_list->AddRect(ImTrunc(vtxs_rect.Min), ImTrunc(vtxs_rect.Max), IM_COL32(0, 255, 255, 255)); // In cyan: bounding box of triangles\n    }\n    out_draw_list->Flags = backup_flags;\n}\n\n// [DEBUG] Display details for a single font, called by ShowStyleEditor().\nvoid ImGui::DebugNodeFont(ImFont* font)\n{\n    bool opened = TreeNode(font, \"Font: \\\"%s\\\"\\n%.2f px, %d glyphs, %d file(s)\",\n        font->ConfigData ? font->ConfigData[0].Name : \"\", font->FontSize, font->Glyphs.Size, font->ConfigDataCount);\n    SameLine();\n    if (SmallButton(\"Set as default\"))\n        GetIO().FontDefault = font;\n    if (!opened)\n        return;\n\n    // Display preview text\n    PushFont(font);\n    Text(\"The quick brown fox jumps over the lazy dog\");\n    PopFont();\n\n    // Display details\n    SetNextItemWidth(GetFontSize() * 8);\n    DragFloat(\"Font scale\", &font->Scale, 0.005f, 0.3f, 2.0f, \"%.1f\");\n    SameLine(); MetricsHelpMarker(\n        \"Note than the default embedded font is NOT meant to be scaled.\\n\\n\"\n        \"Font are currently rendered into bitmaps at a given size at the time of building the atlas. \"\n        \"You may oversample them to get some flexibility with scaling. \"\n        \"You can also render at multiple sizes and select which one to use at runtime.\\n\\n\"\n        \"(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)\");\n    Text(\"Ascent: %f, Descent: %f, Height: %f\", font->Ascent, font->Descent, font->Ascent - font->Descent);\n    char c_str[5];\n    Text(\"Fallback character: '%s' (U+%04X)\", ImTextCharToUtf8(c_str, font->FallbackChar), font->FallbackChar);\n    Text(\"Ellipsis character: '%s' (U+%04X)\", ImTextCharToUtf8(c_str, font->EllipsisChar), font->EllipsisChar);\n    const int surface_sqrt = (int)ImSqrt((float)font->MetricsTotalSurface);\n    Text(\"Texture Area: about %d px ~%dx%d px\", font->MetricsTotalSurface, surface_sqrt, surface_sqrt);\n    for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)\n        if (font->ConfigData)\n            if (const ImFontConfig* cfg = &font->ConfigData[config_i])\n                BulletText(\"Input %d: \\'%s\\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)\",\n                    config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y);\n\n    // Display all glyphs of the fonts in separate pages of 256 characters\n    if (TreeNode(\"Glyphs\", \"Glyphs (%d)\", font->Glyphs.Size))\n    {\n        ImDrawList* draw_list = GetWindowDrawList();\n        const ImU32 glyph_col = GetColorU32(ImGuiCol_Text);\n        const float cell_size = font->FontSize * 1;\n        const float cell_spacing = GetStyle().ItemSpacing.y;\n        for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256)\n        {\n            // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k)\n            // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT\n            // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here)\n            if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095))\n            {\n                base += 4096 - 256;\n                continue;\n            }\n\n            int count = 0;\n            for (unsigned int n = 0; n < 256; n++)\n                if (font->FindGlyphNoFallback((ImWchar)(base + n)))\n                    count++;\n            if (count <= 0)\n                continue;\n            if (!TreeNode((void*)(intptr_t)base, \"U+%04X..U+%04X (%d %s)\", base, base + 255, count, count > 1 ? \"glyphs\" : \"glyph\"))\n                continue;\n\n            // Draw a 16x16 grid of glyphs\n            ImVec2 base_pos = GetCursorScreenPos();\n            for (unsigned int n = 0; n < 256; n++)\n            {\n                // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions\n                // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string.\n                ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing));\n                ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size);\n                const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n));\n                draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50));\n                if (!glyph)\n                    continue;\n                font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n));\n                if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip())\n                {\n                    DebugNodeFontGlyph(font, glyph);\n                    EndTooltip();\n                }\n            }\n            Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16));\n            TreePop();\n        }\n        TreePop();\n    }\n    TreePop();\n}\n\nvoid ImGui::DebugNodeFontGlyph(ImFont*, const ImFontGlyph* glyph)\n{\n    Text(\"Codepoint: U+%04X\", glyph->Codepoint);\n    Separator();\n    Text(\"Visible: %d\", glyph->Visible);\n    Text(\"AdvanceX: %.1f\", glyph->AdvanceX);\n    Text(\"Pos: (%.2f,%.2f)->(%.2f,%.2f)\", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1);\n    Text(\"UV: (%.3f,%.3f)->(%.3f,%.3f)\", glyph->U0, glyph->V0, glyph->U1, glyph->V1);\n}\n\n// [DEBUG] Display contents of ImGuiStorage\nvoid ImGui::DebugNodeStorage(ImGuiStorage* storage, const char* label)\n{\n    if (!TreeNode(label, \"%s: %d entries, %d bytes\", label, storage->Data.Size, storage->Data.size_in_bytes()))\n        return;\n    for (const ImGuiStorage::ImGuiStoragePair& p : storage->Data)\n        BulletText(\"Key 0x%08X Value { i: %d }\", p.key, p.val_i); // Important: we currently don't store a type, real value may not be integer.\n    TreePop();\n}\n\n// [DEBUG] Display contents of ImGuiTabBar\nvoid ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label)\n{\n    // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings.\n    char buf[256];\n    char* p = buf;\n    const char* buf_end = buf + IM_ARRAYSIZE(buf);\n    const bool is_active = (tab_bar->PrevFrameVisible >= GetFrameCount() - 2);\n    p += ImFormatString(p, buf_end - p, \"%s 0x%08X (%d tabs)%s  {\", label, tab_bar->ID, tab_bar->Tabs.Size, is_active ? \"\" : \" *Inactive*\");\n    for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++)\n    {\n        ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];\n        p += ImFormatString(p, buf_end - p, \"%s'%s'\", tab_n > 0 ? \", \" : \"\", TabBarGetTabName(tab_bar, tab));\n    }\n    p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? \" ... }\" : \" } \");\n    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }\n    bool open = TreeNode(label, \"%s\", buf);\n    if (!is_active) { PopStyleColor(); }\n    if (is_active && IsItemHovered())\n    {\n        ImDrawList* draw_list = GetForegroundDrawList();\n        draw_list->AddRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, IM_COL32(255, 255, 0, 255));\n        draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMinX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));\n        draw_list->AddLine(ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Min.y), ImVec2(tab_bar->ScrollingRectMaxX, tab_bar->BarRect.Max.y), IM_COL32(0, 255, 0, 255));\n    }\n    if (open)\n    {\n        for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)\n        {\n            ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];\n            PushID(tab);\n            if (SmallButton(\"<\")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2);\n            if (SmallButton(\">\")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine();\n            Text(\"%02d%c Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f\",\n                tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, TabBarGetTabName(tab_bar, tab), tab->Offset, tab->Width, tab->ContentWidth);\n            PopID();\n        }\n        TreePop();\n    }\n}\n\nvoid ImGui::DebugNodeViewport(ImGuiViewportP* viewport)\n{\n    SetNextItemOpen(true, ImGuiCond_Once);\n    if (TreeNode(\"viewport0\", \"Viewport #%d\", 0))\n    {\n        ImGuiWindowFlags flags = viewport->Flags;\n        BulletText(\"Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\",\n            viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y,\n            viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y);\n        BulletText(\"Flags: 0x%04X =%s%s%s\", viewport->Flags,\n            (flags & ImGuiViewportFlags_IsPlatformWindow)  ? \" IsPlatformWindow\"  : \"\",\n            (flags & ImGuiViewportFlags_IsPlatformMonitor) ? \" IsPlatformMonitor\" : \"\",\n            (flags & ImGuiViewportFlags_OwnedByApp)        ? \" OwnedByApp\"        : \"\");\n        for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists)\n            DebugNodeDrawList(NULL, viewport, draw_list, \"DrawList\");\n        TreePop();\n    }\n}\n\nvoid ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label)\n{\n    if (window == NULL)\n    {\n        BulletText(\"%s: NULL\", label);\n        return;\n    }\n\n    ImGuiContext& g = *GImGui;\n    const bool is_active = window->WasActive;\n    ImGuiTreeNodeFlags tree_node_flags = (window == g.NavWindow) ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None;\n    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }\n    const bool open = TreeNodeEx(label, tree_node_flags, \"%s '%s'%s\", label, window->Name, is_active ? \"\" : \" *Inactive*\");\n    if (!is_active) { PopStyleColor(); }\n    if (IsItemHovered() && is_active)\n        GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255));\n    if (!open)\n        return;\n\n    if (window->MemoryCompacted)\n        TextDisabled(\"Note: some memory buffers have been compacted/freed.\");\n\n    ImGuiWindowFlags flags = window->Flags;\n    DebugNodeDrawList(window, window->Viewport, window->DrawList, \"DrawList\");\n    BulletText(\"Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f) Ideal (%.1f,%.1f)\", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y, window->ContentSizeIdeal.x, window->ContentSizeIdeal.y);\n    BulletText(\"Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)\", flags,\n        (flags & ImGuiWindowFlags_ChildWindow)  ? \"Child \" : \"\",      (flags & ImGuiWindowFlags_Tooltip)     ? \"Tooltip \"   : \"\",  (flags & ImGuiWindowFlags_Popup) ? \"Popup \" : \"\",\n        (flags & ImGuiWindowFlags_Modal)        ? \"Modal \" : \"\",      (flags & ImGuiWindowFlags_ChildMenu)   ? \"ChildMenu \" : \"\",  (flags & ImGuiWindowFlags_NoSavedSettings) ? \"NoSavedSettings \" : \"\",\n        (flags & ImGuiWindowFlags_NoMouseInputs)? \"NoMouseInputs\":\"\", (flags & ImGuiWindowFlags_NoNavInputs) ? \"NoNavInputs\" : \"\", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? \"AlwaysAutoResize\" : \"\");\n    BulletText(\"Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s\", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? \"X\" : \"\", window->ScrollbarY ? \"Y\" : \"\");\n    BulletText(\"Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d\", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1);\n    BulletText(\"Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d\", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems);\n    for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)\n    {\n        ImRect r = window->NavRectRel[layer];\n        if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y)\n            BulletText(\"NavLastIds[%d]: 0x%08X\", layer, window->NavLastIds[layer]);\n        else\n            BulletText(\"NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)\", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y);\n        DebugLocateItemOnHover(window->NavLastIds[layer]);\n    }\n    const ImVec2* pr = window->NavPreferredScoringPosRel;\n    for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++)\n        BulletText(\"NavPreferredScoringPosRel[%d] = {%.1f,%.1f)\", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater.\n    BulletText(\"NavLayersActiveMask: %X, NavLastChildNavWindow: %s\", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : \"NULL\");\n    if (window->RootWindow != window)       { DebugNodeWindow(window->RootWindow, \"RootWindow\"); }\n    if (window->ParentWindow != NULL)       { DebugNodeWindow(window->ParentWindow, \"ParentWindow\"); }\n    if (window->DC.ChildWindows.Size > 0)   { DebugNodeWindowsList(&window->DC.ChildWindows, \"ChildWindows\"); }\n    if (window->ColumnsStorage.Size > 0 && TreeNode(\"Columns\", \"Columns sets (%d)\", window->ColumnsStorage.Size))\n    {\n        for (ImGuiOldColumns& columns : window->ColumnsStorage)\n            DebugNodeColumns(&columns);\n        TreePop();\n    }\n    DebugNodeStorage(&window->StateStorage, \"Storage\");\n    TreePop();\n}\n\nvoid ImGui::DebugNodeWindowSettings(ImGuiWindowSettings* settings)\n{\n    if (settings->WantDelete)\n        BeginDisabled();\n    Text(\"0x%08X \\\"%s\\\" Pos (%d,%d) Size (%d,%d) Collapsed=%d\",\n        settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed);\n    if (settings->WantDelete)\n        EndDisabled();\n}\n\nvoid ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label)\n{\n    if (!TreeNode(label, \"%s (%d)\", label, windows->Size))\n        return;\n    for (int i = windows->Size - 1; i >= 0; i--) // Iterate front to back\n    {\n        PushID((*windows)[i]);\n        DebugNodeWindow((*windows)[i], \"Window\");\n        PopID();\n    }\n    TreePop();\n}\n\n// FIXME-OPT: This is technically suboptimal, but it is simpler this way.\nvoid ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack)\n{\n    for (int i = 0; i < windows_size; i++)\n    {\n        ImGuiWindow* window = windows[i];\n        if (window->ParentWindowInBeginStack != parent_in_begin_stack)\n            continue;\n        char buf[20];\n        ImFormatString(buf, IM_ARRAYSIZE(buf), \"[%04d] Window\", window->BeginOrderWithinContext);\n        //BulletText(\"[%04d] Window '%s'\", window->BeginOrderWithinContext, window->Name);\n        DebugNodeWindow(window, buf);\n        Indent();\n        DebugNodeWindowsListByBeginStackParent(windows + i + 1, windows_size - i - 1, window);\n        Unindent();\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] DEBUG LOG WINDOW\n//-----------------------------------------------------------------------------\n\nvoid ImGui::DebugLog(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    DebugLogV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::DebugLogV(const char* fmt, va_list args)\n{\n    ImGuiContext& g = *GImGui;\n    const int old_size = g.DebugLogBuf.size();\n    g.DebugLogBuf.appendf(\"[%05d] \", g.FrameCount);\n    g.DebugLogBuf.appendfv(fmt, args);\n    g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size());\n    if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY)\n        IMGUI_DEBUG_PRINTF(\"%s\", g.DebugLogBuf.begin() + old_size);\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTestEngine)\n        IMGUI_TEST_ENGINE_LOG(\"%s\", g.DebugLogBuf.begin() + old_size);\n#endif\n}\n\nvoid ImGui::ShowDebugLogWindow(bool* p_open)\n{\n    ImGuiContext& g = *GImGui;\n    if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize))\n        SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 12.0f), ImGuiCond_FirstUseEver);\n    if (!Begin(\"Dear ImGui Debug Log\", p_open) || GetCurrentWindow()->BeginCount > 1)\n    {\n        End();\n        return;\n    }\n\n    CheckboxFlags(\"All\", &g.DebugLogFlags, ImGuiDebugLogFlags_EventMask_);\n    SameLine(); CheckboxFlags(\"ActiveId\", &g.DebugLogFlags, ImGuiDebugLogFlags_EventActiveId);\n    SameLine(); CheckboxFlags(\"Focus\", &g.DebugLogFlags, ImGuiDebugLogFlags_EventFocus);\n    SameLine(); CheckboxFlags(\"Popup\", &g.DebugLogFlags, ImGuiDebugLogFlags_EventPopup);\n    SameLine(); CheckboxFlags(\"Nav\", &g.DebugLogFlags, ImGuiDebugLogFlags_EventNav);\n    SameLine(); if (CheckboxFlags(\"Clipper\", &g.DebugLogFlags, ImGuiDebugLogFlags_EventClipper)) { g.DebugLogClipperAutoDisableFrames = 2; } if (IsItemHovered()) SetTooltip(\"Clipper log auto-disabled after 2 frames\");\n    //SameLine(); CheckboxFlags(\"Selection\", &g.DebugLogFlags, ImGuiDebugLogFlags_EventSelection);\n    SameLine(); CheckboxFlags(\"IO\", &g.DebugLogFlags, ImGuiDebugLogFlags_EventIO);\n\n    if (SmallButton(\"Clear\"))\n    {\n        g.DebugLogBuf.clear();\n        g.DebugLogIndex.clear();\n    }\n    SameLine();\n    if (SmallButton(\"Copy\"))\n        SetClipboardText(g.DebugLogBuf.c_str());\n    BeginChild(\"##log\", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar);\n\n    ImGuiListClipper clipper;\n    clipper.Begin(g.DebugLogIndex.size());\n    while (clipper.Step())\n        for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)\n        {\n            const char* line_begin = g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no);\n            const char* line_end = g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no);\n            TextUnformatted(line_begin, line_end);\n            ImRect text_rect = g.LastItemData.Rect;\n            if (IsItemHovered())\n                for (const char* p = line_begin; p <= line_end - 10; p++)\n                {\n                    ImGuiID id = 0;\n                    if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, \"%X\", &id) != 1)\n                        continue;\n                    ImVec2 p0 = CalcTextSize(line_begin, p);\n                    ImVec2 p1 = CalcTextSize(p, p + 10);\n                    g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y));\n                    if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true))\n                        DebugLocateItemOnHover(id);\n                    p += 10;\n                }\n        }\n    if (GetScrollY() >= GetScrollMaxY())\n        SetScrollHereY(1.0f);\n    EndChild();\n\n    End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, ID STACK TOOL)\n//-----------------------------------------------------------------------------\n\n// Draw a small cross at current CursorPos in current window's DrawList\nvoid ImGui::DebugDrawCursorPos(ImU32 col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImVec2 pos = window->DC.CursorPos;\n    window->DrawList->AddLine(ImVec2(pos.x, pos.y - 3.0f), ImVec2(pos.x, pos.y + 4.0f), col, 1.0f);\n    window->DrawList->AddLine(ImVec2(pos.x - 3.0f, pos.y), ImVec2(pos.x + 4.0f, pos.y), col, 1.0f);\n}\n\n// Draw a 10px wide rectangle around CurposPos.x using Line Y1/Y2 in current window's DrawList\nvoid ImGui::DebugDrawLineExtents(ImU32 col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    float curr_x = window->DC.CursorPos.x;\n    float line_y1 = (window->DC.IsSameLine ? window->DC.CursorPosPrevLine.y : window->DC.CursorPos.y);\n    float line_y2 = line_y1 + (window->DC.IsSameLine ? window->DC.PrevLineSize.y : window->DC.CurrLineSize.y);\n    window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y1), ImVec2(curr_x + 5.0f, line_y1), col, 1.0f);\n    window->DrawList->AddLine(ImVec2(curr_x - 0.5f, line_y1), ImVec2(curr_x - 0.5f, line_y2), col, 1.0f);\n    window->DrawList->AddLine(ImVec2(curr_x - 5.0f, line_y2), ImVec2(curr_x + 5.0f, line_y2), col, 1.0f);\n}\n\n// Draw last item rect in ForegroundDrawList (so it is always visible)\nvoid ImGui::DebugDrawItemRect(ImU32 col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col);\n}\n\n// [DEBUG] Locate item position/rectangle given an ID.\nstatic const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255);  // Green\n\nvoid ImGui::DebugLocateItem(ImGuiID target_id)\n{\n    ImGuiContext& g = *GImGui;\n    g.DebugLocateId = target_id;\n    g.DebugLocateFrames = 2;\n}\n\nvoid ImGui::DebugLocateItemOnHover(ImGuiID target_id)\n{\n    if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup))\n        return;\n    ImGuiContext& g = *GImGui;\n    DebugLocateItem(target_id);\n    GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR);\n}\n\nvoid ImGui::DebugLocateItemResolveWithLastItem()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiLastItemData item_data = g.LastItemData;\n    g.DebugLocateId = 0;\n    ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow);\n    ImRect r = item_data.Rect;\n    r.Expand(3.0f);\n    ImVec2 p1 = g.IO.MousePos;\n    ImVec2 p2 = ImVec2((p1.x < r.Min.x) ? r.Min.x : (p1.x > r.Max.x) ? r.Max.x : p1.x, (p1.y < r.Min.y) ? r.Min.y : (p1.y > r.Max.y) ? r.Max.y : p1.y);\n    draw_list->AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR);\n    draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR);\n}\n\n// [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack.\nvoid ImGui::UpdateDebugToolItemPicker()\n{\n    ImGuiContext& g = *GImGui;\n    g.DebugItemPickerBreakId = 0;\n    if (!g.DebugItemPickerActive)\n        return;\n\n    const ImGuiID hovered_id = g.HoveredIdPreviousFrame;\n    SetMouseCursor(ImGuiMouseCursor_Hand);\n    if (IsKeyPressed(ImGuiKey_Escape))\n        g.DebugItemPickerActive = false;\n    const bool change_mapping = g.IO.KeyMods == (ImGuiMod_Ctrl | ImGuiMod_Shift);\n    if (!change_mapping && IsMouseClicked(g.DebugItemPickerMouseButton) && hovered_id)\n    {\n        g.DebugItemPickerBreakId = hovered_id;\n        g.DebugItemPickerActive = false;\n    }\n    for (int mouse_button = 0; mouse_button < 3; mouse_button++)\n        if (change_mapping && IsMouseClicked(mouse_button))\n            g.DebugItemPickerMouseButton = (ImU8)mouse_button;\n    SetNextWindowBgAlpha(0.70f);\n    if (!BeginTooltip())\n        return;\n    Text(\"HoveredId: 0x%08X\", hovered_id);\n    Text(\"Press ESC to abort picking.\");\n    const char* mouse_button_names[] = { \"Left\", \"Right\", \"Middle\" };\n    if (change_mapping)\n        Text(\"Remap w/ Ctrl+Shift: click anywhere to select new mouse button.\");\n    else\n        TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), \"Click %s Button to break in debugger! (remap w/ Ctrl+Shift)\", mouse_button_names[g.DebugItemPickerMouseButton]);\n    EndTooltip();\n}\n\n// [DEBUG] ID Stack Tool: update queries. Called by NewFrame()\nvoid ImGui::UpdateDebugToolStackQueries()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiIDStackTool* tool = &g.DebugIDStackTool;\n\n    // Clear hook when id stack tool is not visible\n    g.DebugHookIdInfo = 0;\n    if (g.FrameCount != tool->LastActiveFrame + 1)\n        return;\n\n    // Update queries. The steps are: -1: query Stack, >= 0: query each stack item\n    // We can only perform 1 ID Info query every frame. This is designed so the GetID() tests are cheap and constant-time\n    const ImGuiID query_id = g.HoveredIdPreviousFrame ? g.HoveredIdPreviousFrame : g.ActiveId;\n    if (tool->QueryId != query_id)\n    {\n        tool->QueryId = query_id;\n        tool->StackLevel = -1;\n        tool->Results.resize(0);\n    }\n    if (query_id == 0)\n        return;\n\n    // Advance to next stack level when we got our result, or after 2 frames (in case we never get a result)\n    int stack_level = tool->StackLevel;\n    if (stack_level >= 0 && stack_level < tool->Results.Size)\n        if (tool->Results[stack_level].QuerySuccess || tool->Results[stack_level].QueryFrameCount > 2)\n            tool->StackLevel++;\n\n    // Update hook\n    stack_level = tool->StackLevel;\n    if (stack_level == -1)\n        g.DebugHookIdInfo = query_id;\n    if (stack_level >= 0 && stack_level < tool->Results.Size)\n    {\n        g.DebugHookIdInfo = tool->Results[stack_level].ID;\n        tool->Results[stack_level].QueryFrameCount++;\n    }\n}\n\n// [DEBUG] ID Stack tool: hooks called by GetID() family functions\nvoid ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiIDStackTool* tool = &g.DebugIDStackTool;\n\n    // Step 0: stack query\n    // This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget.\n    if (tool->StackLevel == -1)\n    {\n        tool->StackLevel++;\n        tool->Results.resize(window->IDStack.Size + 1, ImGuiStackLevelInfo());\n        for (int n = 0; n < window->IDStack.Size + 1; n++)\n            tool->Results[n].ID = (n < window->IDStack.Size) ? window->IDStack[n] : id;\n        return;\n    }\n\n    // Step 1+: query for individual level\n    IM_ASSERT(tool->StackLevel >= 0);\n    if (tool->StackLevel != window->IDStack.Size)\n        return;\n    ImGuiStackLevelInfo* info = &tool->Results[tool->StackLevel];\n    IM_ASSERT(info->ID == id && info->QueryFrameCount > 0);\n\n    switch (data_type)\n    {\n    case ImGuiDataType_S32:\n        ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), \"%d\", (int)(intptr_t)data_id);\n        break;\n    case ImGuiDataType_String:\n        ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), \"%.*s\", data_id_end ? (int)((const char*)data_id_end - (const char*)data_id) : (int)strlen((const char*)data_id), (const char*)data_id);\n        break;\n    case ImGuiDataType_Pointer:\n        ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), \"(void*)0x%p\", data_id);\n        break;\n    case ImGuiDataType_ID:\n        if (info->Desc[0] != 0) // PushOverrideID() is often used to avoid hashing twice, which would lead to 2 calls to DebugHookIdInfo(). We prioritize the first one.\n            return;\n        ImFormatString(info->Desc, IM_ARRAYSIZE(info->Desc), \"0x%08X [override]\", id);\n        break;\n    default:\n        IM_ASSERT(0);\n    }\n    info->QuerySuccess = true;\n    info->DataType = data_type;\n}\n\nstatic int StackToolFormatLevelInfo(ImGuiIDStackTool* tool, int n, bool format_for_ui, char* buf, size_t buf_size)\n{\n    ImGuiStackLevelInfo* info = &tool->Results[n];\n    ImGuiWindow* window = (info->Desc[0] == 0 && n == 0) ? ImGui::FindWindowByID(info->ID) : NULL;\n    if (window)                                                                 // Source: window name (because the root ID don't call GetID() and so doesn't get hooked)\n        return ImFormatString(buf, buf_size, format_for_ui ? \"\\\"%s\\\" [window]\" : \"%s\", window->Name);\n    if (info->QuerySuccess)                                                     // Source: GetID() hooks (prioritize over ItemInfo() because we frequently use patterns like: PushID(str), Button(\"\") where they both have same id)\n        return ImFormatString(buf, buf_size, (format_for_ui && info->DataType == ImGuiDataType_String) ? \"\\\"%s\\\"\" : \"%s\", info->Desc);\n    if (tool->StackLevel < tool->Results.Size)                                  // Only start using fallback below when all queries are done, so during queries we don't flickering ??? markers.\n        return (*buf = 0);\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    if (const char* label = ImGuiTestEngine_FindItemDebugLabel(GImGui, info->ID))   // Source: ImGuiTestEngine's ItemInfo()\n        return ImFormatString(buf, buf_size, format_for_ui ? \"??? \\\"%s\\\"\" : \"%s\", label);\n#endif\n    return ImFormatString(buf, buf_size, \"???\");\n}\n\n// ID Stack Tool: Display UI\nvoid ImGui::ShowIDStackToolWindow(bool* p_open)\n{\n    ImGuiContext& g = *GImGui;\n    if (!(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize))\n        SetNextWindowSize(ImVec2(0.0f, GetFontSize() * 8.0f), ImGuiCond_FirstUseEver);\n    if (!Begin(\"Dear ImGui ID Stack Tool\", p_open) || GetCurrentWindow()->BeginCount > 1)\n    {\n        End();\n        return;\n    }\n\n    // Display hovered/active status\n    ImGuiIDStackTool* tool = &g.DebugIDStackTool;\n    const ImGuiID hovered_id = g.HoveredIdPreviousFrame;\n    const ImGuiID active_id = g.ActiveId;\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    Text(\"HoveredId: 0x%08X (\\\"%s\\\"), ActiveId:  0x%08X (\\\"%s\\\")\", hovered_id, hovered_id ? ImGuiTestEngine_FindItemDebugLabel(&g, hovered_id) : \"\", active_id, active_id ? ImGuiTestEngine_FindItemDebugLabel(&g, active_id) : \"\");\n#else\n    Text(\"HoveredId: 0x%08X, ActiveId:  0x%08X\", hovered_id, active_id);\n#endif\n    SameLine();\n    MetricsHelpMarker(\"Hover an item with the mouse to display elements of the ID Stack leading to the item's final ID.\\nEach level of the stack correspond to a PushID() call.\\nAll levels of the stack are hashed together to make the final ID of a widget (ID displayed at the bottom level of the stack).\\nRead FAQ entry about the ID stack for details.\");\n\n    // CTRL+C to copy path\n    const float time_since_copy = (float)g.Time - tool->CopyToClipboardLastTime;\n    Checkbox(\"Ctrl+C: copy path to clipboard\", &tool->CopyToClipboardOnCtrlC);\n    SameLine();\n    TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), \"*COPIED*\");\n    if (tool->CopyToClipboardOnCtrlC && IsKeyDown(ImGuiMod_Ctrl) && IsKeyPressed(ImGuiKey_C))\n    {\n        tool->CopyToClipboardLastTime = (float)g.Time;\n        char* p = g.TempBuffer.Data;\n        char* p_end = p + g.TempBuffer.Size;\n        for (int stack_n = 0; stack_n < tool->Results.Size && p + 3 < p_end; stack_n++)\n        {\n            *p++ = '/';\n            char level_desc[256];\n            StackToolFormatLevelInfo(tool, stack_n, false, level_desc, IM_ARRAYSIZE(level_desc));\n            for (int n = 0; level_desc[n] && p + 2 < p_end; n++)\n            {\n                if (level_desc[n] == '/')\n                    *p++ = '\\\\';\n                *p++ = level_desc[n];\n            }\n        }\n        *p = '\\0';\n        SetClipboardText(g.TempBuffer.Data);\n    }\n\n    // Display decorated stack\n    tool->LastActiveFrame = g.FrameCount;\n    if (tool->Results.Size > 0 && BeginTable(\"##table\", 3, ImGuiTableFlags_Borders))\n    {\n        const float id_width = CalcTextSize(\"0xDDDDDDDD\").x;\n        TableSetupColumn(\"Seed\", ImGuiTableColumnFlags_WidthFixed, id_width);\n        TableSetupColumn(\"PushID\", ImGuiTableColumnFlags_WidthStretch);\n        TableSetupColumn(\"Result\", ImGuiTableColumnFlags_WidthFixed, id_width);\n        TableHeadersRow();\n        for (int n = 0; n < tool->Results.Size; n++)\n        {\n            ImGuiStackLevelInfo* info = &tool->Results[n];\n            TableNextColumn();\n            Text(\"0x%08X\", (n > 0) ? tool->Results[n - 1].ID : 0);\n            TableNextColumn();\n            StackToolFormatLevelInfo(tool, n, true, g.TempBuffer.Data, g.TempBuffer.Size);\n            TextUnformatted(g.TempBuffer.Data);\n            TableNextColumn();\n            Text(\"0x%08X\", info->ID);\n            if (n == tool->Results.Size - 1)\n                TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_Header));\n        }\n        EndTable();\n    }\n    End();\n}\n\n#else\n\nvoid ImGui::ShowMetricsWindow(bool*) {}\nvoid ImGui::ShowFontAtlas(ImFontAtlas*) {}\nvoid ImGui::DebugNodeColumns(ImGuiOldColumns*) {}\nvoid ImGui::DebugNodeDrawList(ImGuiWindow*, ImGuiViewportP*, const ImDrawList*, const char*) {}\nvoid ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList*, const ImDrawList*, const ImDrawCmd*, bool, bool) {}\nvoid ImGui::DebugNodeFont(ImFont*) {}\nvoid ImGui::DebugNodeStorage(ImGuiStorage*, const char*) {}\nvoid ImGui::DebugNodeTabBar(ImGuiTabBar*, const char*) {}\nvoid ImGui::DebugNodeWindow(ImGuiWindow*, const char*) {}\nvoid ImGui::DebugNodeWindowSettings(ImGuiWindowSettings*) {}\nvoid ImGui::DebugNodeWindowsList(ImVector<ImGuiWindow*>*, const char*) {}\nvoid ImGui::DebugNodeViewport(ImGuiViewportP*) {}\n\nvoid ImGui::DebugLog(const char*, ...) {}\nvoid ImGui::DebugLogV(const char*, va_list) {}\nvoid ImGui::ShowDebugLogWindow(bool*) {}\nvoid ImGui::ShowIDStackToolWindow(bool*) {}\nvoid ImGui::DebugHookIdInfo(ImGuiID, ImGuiDataType, const void*, const void*) {}\nvoid ImGui::UpdateDebugToolItemPicker() {}\nvoid ImGui::UpdateDebugToolStackQueries() {}\n\n#endif // #ifndef IMGUI_DISABLE_DEBUG_TOOLS\n\n//-----------------------------------------------------------------------------\n\n// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed.\n// Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github.\n#ifdef IMGUI_INCLUDE_IMGUI_USER_INL\n#include \"imgui_user.inl\"\n#endif\n\n//-----------------------------------------------------------------------------\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "third-party/imgui/imgui.h",
    "content": "// dear imgui, v1.90.0\n// (headers)\n\n// Help:\n// - See links below.\n// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.\n// - Read top of imgui.cpp for more details, links and comments.\n\n// Resources:\n// - FAQ                   https://dearimgui.com/faq\n// - Getting Started       https://dearimgui.com/getting-started\n// - Homepage              https://github.com/ocornut/imgui\n// - Releases & changelog  https://github.com/ocornut/imgui/releases\n// - Gallery               https://github.com/ocornut/imgui/issues/6897 (please post your screenshots/video there!)\n// - Wiki                  https://github.com/ocornut/imgui/wiki (lots of good stuff there)\n// - Glossary              https://github.com/ocornut/imgui/wiki/Glossary\n// - Issues & support      https://github.com/ocornut/imgui/issues\n// - Tests & Automation    https://github.com/ocornut/imgui_test_engine\n\n// For first-time users having issues compiling/linking/running/loading fonts:\n// please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above.\n// Everything else should be asked in 'Issues'! We are building a database of cross-linked knowledge there.\n\n// Library Version\n// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')\n#define IMGUI_VERSION       \"1.90.0\"\n#define IMGUI_VERSION_NUM   19000\n#define IMGUI_HAS_TABLE\n\n/*\n\nIndex of this file:\n// [SECTION] Header mess\n// [SECTION] Forward declarations and basic types\n// [SECTION] Dear ImGui end-user API functions\n// [SECTION] Flags & Enumerations\n// [SECTION] Helpers: Memory allocations macros, ImVector<>\n// [SECTION] ImGuiStyle\n// [SECTION] ImGuiIO\n// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload, ImGuiTableSortSpecs, ImGuiTableColumnSortSpecs)\n// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor)\n// [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData)\n// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont)\n// [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport)\n// [SECTION] Platform Dependent Interfaces (ImGuiPlatformImeData)\n// [SECTION] Obsolete functions and types\n\n*/\n\n#pragma once\n\n// Configuration file with compile-time options\n// (edit imconfig.h or '#define IMGUI_USER_CONFIG \"myfilename.h\" from your build system)\n#ifdef IMGUI_USER_CONFIG\n#include IMGUI_USER_CONFIG\n#endif\n#include \"imconfig.h\"\n\n#ifndef IMGUI_DISABLE\n\n//-----------------------------------------------------------------------------\n// [SECTION] Header mess\n//-----------------------------------------------------------------------------\n\n// Includes\n#include <float.h>                  // FLT_MIN, FLT_MAX\n#include <stdarg.h>                 // va_list, va_start, va_end\n#include <stddef.h>                 // ptrdiff_t, NULL\n#include <string.h>                 // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp\n\n// Define attributes of all API symbols declarations (e.g. for DLL under Windows)\n// IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default backends files (imgui_impl_xxx.h)\n// Using dear imgui via a shared library is not recommended: we don't guarantee backward nor forward ABI compatibility + this is a call-heavy library and function call overhead adds up.\n#ifndef IMGUI_API\n#define IMGUI_API\n#endif\n#ifndef IMGUI_IMPL_API\n#define IMGUI_IMPL_API              IMGUI_API\n#endif\n\n// Helper Macros\n#ifndef IM_ASSERT\n#include <assert.h>\n#define IM_ASSERT(_EXPR)            assert(_EXPR)                               // You can override the default assert handler by editing imconfig.h\n#endif\n#define IM_ARRAYSIZE(_ARR)          ((int)(sizeof(_ARR) / sizeof(*(_ARR))))     // Size of a static C-style array. Don't use on pointers!\n#define IM_UNUSED(_VAR)             ((void)(_VAR))                              // Used to silence \"unused variable warnings\". Often useful as asserts may be stripped out from final builds.\n#define IMGUI_CHECKVERSION()        ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))\n\n// Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions.\n#if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__)\n#define IM_FMTARGS(FMT)             __attribute__((format(gnu_printf, FMT, FMT+1)))\n#define IM_FMTLIST(FMT)             __attribute__((format(gnu_printf, FMT, 0)))\n#elif !defined(IMGUI_USE_STB_SPRINTF) && (defined(__clang__) || defined(__GNUC__))\n#define IM_FMTARGS(FMT)             __attribute__((format(printf, FMT, FMT+1)))\n#define IM_FMTLIST(FMT)             __attribute__((format(printf, FMT, 0)))\n#else\n#define IM_FMTARGS(FMT)\n#define IM_FMTLIST(FMT)\n#endif\n\n// Disable some of MSVC most aggressive Debug runtime checks in function header/footer (used in some simple/low-level functions)\n#if defined(_MSC_VER) && !defined(__clang__)  && !defined(__INTEL_COMPILER) && !defined(IMGUI_DEBUG_PARANOID)\n#define IM_MSVC_RUNTIME_CHECKS_OFF      __pragma(runtime_checks(\"\",off))     __pragma(check_stack(off)) __pragma(strict_gs_check(push,off))\n#define IM_MSVC_RUNTIME_CHECKS_RESTORE  __pragma(runtime_checks(\"\",restore)) __pragma(check_stack())    __pragma(strict_gs_check(pop))\n#else\n#define IM_MSVC_RUNTIME_CHECKS_OFF\n#define IM_MSVC_RUNTIME_CHECKS_RESTORE\n#endif\n\n// Warnings\n#ifdef _MSC_VER\n#pragma warning (push)\n#pragma warning (disable: 26495)    // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).\n#endif\n#if defined(__clang__)\n#pragma clang diagnostic push\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"\n#pragma clang diagnostic ignored \"-Wreserved-identifier\"            // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter\n#elif defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpragmas\"          // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Forward declarations and basic types\n//-----------------------------------------------------------------------------\n\n// Forward declarations\nstruct ImDrawChannel;               // Temporary storage to output draw commands out of order, used by ImDrawListSplitter and ImDrawList::ChannelsSplit()\nstruct ImDrawCmd;                   // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call, unless it is a callback)\nstruct ImDrawData;                  // All draw command lists required to render the frame + pos/size coordinates to use for the projection matrix.\nstruct ImDrawList;                  // A single draw command list (generally one per window, conceptually you may see this as a dynamic \"mesh\" builder)\nstruct ImDrawListSharedData;        // Data shared among multiple draw lists (typically owned by parent ImGui context, but you may create one yourself)\nstruct ImDrawListSplitter;          // Helper to split a draw list into different layers which can be drawn into out of order, then flattened back.\nstruct ImDrawVert;                  // A single vertex (pos + uv + col = 20 bytes by default. Override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT)\nstruct ImFont;                      // Runtime data for a single font within a parent ImFontAtlas\nstruct ImFontAtlas;                 // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF/OTF font loader\nstruct ImFontBuilderIO;             // Opaque interface to a font builder (stb_truetype or FreeType).\nstruct ImFontConfig;                // Configuration data when adding a font or merging fonts\nstruct ImFontGlyph;                 // A single font glyph (code point + coordinates within in ImFontAtlas + offset)\nstruct ImFontGlyphRangesBuilder;    // Helper to build glyph ranges from text/string data\nstruct ImColor;                     // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using)\nstruct ImGuiContext;                // Dear ImGui context (opaque structure, unless including imgui_internal.h)\nstruct ImGuiIO;                     // Main configuration and I/O between your application and ImGui\nstruct ImGuiInputTextCallbackData;  // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use)\nstruct ImGuiKeyData;                // Storage for ImGuiIO and IsKeyDown(), IsKeyPressed() etc functions.\nstruct ImGuiListClipper;            // Helper to manually clip large list of items\nstruct ImGuiOnceUponAFrame;         // Helper for running a block of code not more than once a frame\nstruct ImGuiPayload;                // User data payload for drag and drop operations\nstruct ImGuiPlatformImeData;        // Platform IME data for io.SetPlatformImeDataFn() function.\nstruct ImGuiSizeCallbackData;       // Callback data when using SetNextWindowSizeConstraints() (rare/advanced use)\nstruct ImGuiStorage;                // Helper for key->value storage\nstruct ImGuiStyle;                  // Runtime data for styling/colors\nstruct ImGuiTableSortSpecs;         // Sorting specifications for a table (often handling sort specs for a single column, occasionally more)\nstruct ImGuiTableColumnSortSpecs;   // Sorting specification for one column of a table\nstruct ImGuiTextBuffer;             // Helper to hold and append into a text buffer (~string builder)\nstruct ImGuiTextFilter;             // Helper to parse and apply text filters (e.g. \"aaaaa[,bbbbb][,ccccc]\")\nstruct ImGuiViewport;               // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor\n\n// Enumerations\n// - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration)\n// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!\n//   In Visual Studio IDE: CTRL+comma (\"Edit.GoToAll\") can follow symbols in comments, whereas CTRL+F12 (\"Edit.GoToImplementation\") cannot.\n//   With Visual Assist installed: ALT+G (\"VAssistX.GoToImplementation\") can also follow symbols in comments.\nenum ImGuiKey : int;                // -> enum ImGuiKey              // Enum: A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value)\nenum ImGuiMouseSource : int;        // -> enum ImGuiMouseSource      // Enum; A mouse input source identifier (Mouse, TouchScreen, Pen)\ntypedef int ImGuiCol;               // -> enum ImGuiCol_             // Enum: A color identifier for styling\ntypedef int ImGuiCond;              // -> enum ImGuiCond_            // Enum: A condition for many Set*() functions\ntypedef int ImGuiDataType;          // -> enum ImGuiDataType_        // Enum: A primary data type\ntypedef int ImGuiDir;               // -> enum ImGuiDir_             // Enum: A cardinal direction\ntypedef int ImGuiMouseButton;       // -> enum ImGuiMouseButton_     // Enum: A mouse button identifier (0=left, 1=right, 2=middle)\ntypedef int ImGuiMouseCursor;       // -> enum ImGuiMouseCursor_     // Enum: A mouse cursor shape\ntypedef int ImGuiSortDirection;     // -> enum ImGuiSortDirection_   // Enum: A sorting direction (ascending or descending)\ntypedef int ImGuiStyleVar;          // -> enum ImGuiStyleVar_        // Enum: A variable identifier for styling\ntypedef int ImGuiTableBgTarget;     // -> enum ImGuiTableBgTarget_   // Enum: A color target for TableSetBgColor()\n\n// Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file)\n// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists!\n//   In Visual Studio IDE: CTRL+comma (\"Edit.GoToAll\") can follow symbols in comments, whereas CTRL+F12 (\"Edit.GoToImplementation\") cannot.\n//   With Visual Assist installed: ALT+G (\"VAssistX.GoToImplementation\") can also follow symbols in comments.\ntypedef int ImDrawFlags;            // -> enum ImDrawFlags_          // Flags: for ImDrawList functions\ntypedef int ImDrawListFlags;        // -> enum ImDrawListFlags_      // Flags: for ImDrawList instance\ntypedef int ImFontAtlasFlags;       // -> enum ImFontAtlasFlags_     // Flags: for ImFontAtlas build\ntypedef int ImGuiBackendFlags;      // -> enum ImGuiBackendFlags_    // Flags: for io.BackendFlags\ntypedef int ImGuiButtonFlags;       // -> enum ImGuiButtonFlags_     // Flags: for InvisibleButton()\ntypedef int ImGuiChildFlags;        // -> enum ImGuiChildFlags_      // Flags: for BeginChild()\ntypedef int ImGuiColorEditFlags;    // -> enum ImGuiColorEditFlags_  // Flags: for ColorEdit4(), ColorPicker4() etc.\ntypedef int ImGuiConfigFlags;       // -> enum ImGuiConfigFlags_     // Flags: for io.ConfigFlags\ntypedef int ImGuiComboFlags;        // -> enum ImGuiComboFlags_      // Flags: for BeginCombo()\ntypedef int ImGuiDragDropFlags;     // -> enum ImGuiDragDropFlags_   // Flags: for BeginDragDropSource(), AcceptDragDropPayload()\ntypedef int ImGuiFocusedFlags;      // -> enum ImGuiFocusedFlags_    // Flags: for IsWindowFocused()\ntypedef int ImGuiHoveredFlags;      // -> enum ImGuiHoveredFlags_    // Flags: for IsItemHovered(), IsWindowHovered() etc.\ntypedef int ImGuiInputTextFlags;    // -> enum ImGuiInputTextFlags_  // Flags: for InputText(), InputTextMultiline()\ntypedef int ImGuiKeyChord;          // -> ImGuiKey | ImGuiMod_XXX    // Flags: for storage only for now: an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values.\ntypedef int ImGuiPopupFlags;        // -> enum ImGuiPopupFlags_      // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen()\ntypedef int ImGuiSelectableFlags;   // -> enum ImGuiSelectableFlags_ // Flags: for Selectable()\ntypedef int ImGuiSliderFlags;       // -> enum ImGuiSliderFlags_     // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.\ntypedef int ImGuiTabBarFlags;       // -> enum ImGuiTabBarFlags_     // Flags: for BeginTabBar()\ntypedef int ImGuiTabItemFlags;      // -> enum ImGuiTabItemFlags_    // Flags: for BeginTabItem()\ntypedef int ImGuiTableFlags;        // -> enum ImGuiTableFlags_      // Flags: For BeginTable()\ntypedef int ImGuiTableColumnFlags;  // -> enum ImGuiTableColumnFlags_// Flags: For TableSetupColumn()\ntypedef int ImGuiTableRowFlags;     // -> enum ImGuiTableRowFlags_   // Flags: For TableNextRow()\ntypedef int ImGuiTreeNodeFlags;     // -> enum ImGuiTreeNodeFlags_   // Flags: for TreeNode(), TreeNodeEx(), CollapsingHeader()\ntypedef int ImGuiViewportFlags;     // -> enum ImGuiViewportFlags_   // Flags: for ImGuiViewport\ntypedef int ImGuiWindowFlags;       // -> enum ImGuiWindowFlags_     // Flags: for Begin(), BeginChild()\n\n// ImTexture: user data for renderer backend to identify a texture [Compile-time configurable type]\n// - To use something else than an opaque void* pointer: override with e.g. '#define ImTextureID MyTextureType*' in your imconfig.h file.\n// - This can be whatever to you want it to be! read the FAQ about ImTextureID for details.\n#ifndef ImTextureID\ntypedef void* ImTextureID;          // Default: store a pointer or an integer fitting in a pointer (most renderer backends are ok with that)\n#endif\n\n// ImDrawIdx: vertex index. [Compile-time configurable type]\n// - To use 16-bit indices + allow large meshes: backend need to set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset' and handle ImDrawCmd::VtxOffset (recommended).\n// - To use 32-bit indices: override with '#define ImDrawIdx unsigned int' in your imconfig.h file.\n#ifndef ImDrawIdx\ntypedef unsigned short ImDrawIdx;   // Default: 16-bit (for maximum compatibility with renderer backends)\n#endif\n\n// Scalar data types\ntypedef unsigned int        ImGuiID;// A unique ID used by widgets (typically the result of hashing a stack of string)\ntypedef signed char         ImS8;   // 8-bit signed integer\ntypedef unsigned char       ImU8;   // 8-bit unsigned integer\ntypedef signed short        ImS16;  // 16-bit signed integer\ntypedef unsigned short      ImU16;  // 16-bit unsigned integer\ntypedef signed int          ImS32;  // 32-bit signed integer == int\ntypedef unsigned int        ImU32;  // 32-bit unsigned integer (often used to store packed colors)\ntypedef signed   long long  ImS64;  // 64-bit signed integer\ntypedef unsigned long long  ImU64;  // 64-bit unsigned integer\n\n// Character types\n// (we generally use UTF-8 encoded string in the API. This is storage specifically for a decoded character used for keyboard input and display)\ntypedef unsigned int ImWchar32;     // A single decoded U32 character/code point. We encode them as multi bytes UTF-8 when used in strings.\ntypedef unsigned short ImWchar16;   // A single decoded U16 character/code point. We encode them as multi bytes UTF-8 when used in strings.\n#ifdef IMGUI_USE_WCHAR32            // ImWchar [configurable type: override in imconfig.h with '#define IMGUI_USE_WCHAR32' to support Unicode planes 1-16]\ntypedef ImWchar32 ImWchar;\n#else\ntypedef ImWchar16 ImWchar;\n#endif\n\n// Callback and functions types\ntypedef int     (*ImGuiInputTextCallback)(ImGuiInputTextCallbackData* data);    // Callback function for ImGui::InputText()\ntypedef void    (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data);              // Callback function for ImGui::SetNextWindowSizeConstraints()\ntypedef void*   (*ImGuiMemAllocFunc)(size_t sz, void* user_data);               // Function signature for ImGui::SetAllocatorFunctions()\ntypedef void    (*ImGuiMemFreeFunc)(void* ptr, void* user_data);                // Function signature for ImGui::SetAllocatorFunctions()\n\n// ImVec2: 2D vector used to store positions, sizes etc. [Compile-time configurable type]\n// This is a frequently used type in the API. Consider using IM_VEC2_CLASS_EXTRA to create implicit cast from/to our preferred type.\nIM_MSVC_RUNTIME_CHECKS_OFF\nstruct ImVec2\n{\n    float                                   x, y;\n    constexpr ImVec2()                      : x(0.0f), y(0.0f) { }\n    constexpr ImVec2(float _x, float _y)    : x(_x), y(_y) { }\n    float& operator[] (size_t idx)          { IM_ASSERT(idx == 0 || idx == 1); return ((float*)(void*)(char*)this)[idx]; } // We very rarely use this [] operator, so the assert overhead is fine.\n    float  operator[] (size_t idx) const    { IM_ASSERT(idx == 0 || idx == 1); return ((const float*)(const void*)(const char*)this)[idx]; }\n#ifdef IM_VEC2_CLASS_EXTRA\n    IM_VEC2_CLASS_EXTRA     // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2.\n#endif\n};\n\n// ImVec4: 4D vector used to store clipping rectangles, colors etc. [Compile-time configurable type]\nstruct ImVec4\n{\n    float                                                     x, y, z, w;\n    constexpr ImVec4()                                        : x(0.0f), y(0.0f), z(0.0f), w(0.0f) { }\n    constexpr ImVec4(float _x, float _y, float _z, float _w)  : x(_x), y(_y), z(_z), w(_w) { }\n#ifdef IM_VEC4_CLASS_EXTRA\n    IM_VEC4_CLASS_EXTRA     // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4.\n#endif\n};\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n//-----------------------------------------------------------------------------\n// [SECTION] Dear ImGui end-user API functions\n// (Note that ImGui:: being a namespace, you can add extra ImGui:: functions in your own separate file. Please don't modify imgui source files!)\n//-----------------------------------------------------------------------------\n\nnamespace ImGui\n{\n    // Context creation and access\n    // - Each context create its own ImFontAtlas by default. You may instance one yourself and pass it to CreateContext() to share a font atlas between contexts.\n    // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()\n    //   for each static/DLL boundary you are calling from. Read \"Context and Memory Allocators\" section of imgui.cpp for details.\n    IMGUI_API ImGuiContext* CreateContext(ImFontAtlas* shared_font_atlas = NULL);\n    IMGUI_API void          DestroyContext(ImGuiContext* ctx = NULL);   // NULL = destroy current context\n    IMGUI_API ImGuiContext* GetCurrentContext();\n    IMGUI_API void          SetCurrentContext(ImGuiContext* ctx);\n\n    // Main\n    IMGUI_API ImGuiIO&      GetIO();                                    // access the IO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags)\n    IMGUI_API ImGuiStyle&   GetStyle();                                 // access the Style structure (colors, sizes). Always use PushStyleCol(), PushStyleVar() to modify style mid-frame!\n    IMGUI_API void          NewFrame();                                 // start a new Dear ImGui frame, you can submit any command from this point until Render()/EndFrame().\n    IMGUI_API void          EndFrame();                                 // ends the Dear ImGui frame. automatically called by Render(). If you don't need to render data (skipping rendering) you may call EndFrame() without Render()... but you'll have wasted CPU already! If you don't need to render, better to not create any windows and not call NewFrame() at all!\n    IMGUI_API void          Render();                                   // ends the Dear ImGui frame, finalize the draw data. You can then get call GetDrawData().\n    IMGUI_API ImDrawData*   GetDrawData();                              // valid after Render() and until the next call to NewFrame(). this is what you have to render.\n\n    // Demo, Debug, Information\n    IMGUI_API void          ShowDemoWindow(bool* p_open = NULL);        // create Demo window. demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application!\n    IMGUI_API void          ShowMetricsWindow(bool* p_open = NULL);     // create Metrics/Debugger window. display Dear ImGui internals: windows, draw commands, various internal state, etc.\n    IMGUI_API void          ShowDebugLogWindow(bool* p_open = NULL);    // create Debug Log window. display a simplified log of important dear imgui events.\n    IMGUI_API void          ShowIDStackToolWindow(bool* p_open = NULL); // create Stack Tool window. hover items with mouse to query information about the source of their unique ID.\n    IMGUI_API void          ShowAboutWindow(bool* p_open = NULL);       // create About window. display Dear ImGui version, credits and build/system information.\n    IMGUI_API void          ShowStyleEditor(ImGuiStyle* ref = NULL);    // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style)\n    IMGUI_API bool          ShowStyleSelector(const char* label);       // add style selector block (not a window), essentially a combo listing the default styles.\n    IMGUI_API void          ShowFontSelector(const char* label);        // add font selector block (not a window), essentially a combo listing the loaded fonts.\n    IMGUI_API void          ShowUserGuide();                            // add basic help/info block (not a window): how to manipulate ImGui as an end-user (mouse/keyboard controls).\n    IMGUI_API const char*   GetVersion();                               // get the compiled version string e.g. \"1.80 WIP\" (essentially the value for IMGUI_VERSION from the compiled version of imgui.cpp)\n\n    // Styles\n    IMGUI_API void          StyleColorsDark(ImGuiStyle* dst = NULL);    // new, recommended style (default)\n    IMGUI_API void          StyleColorsLight(ImGuiStyle* dst = NULL);   // best used with borders and a custom, thicker font\n    IMGUI_API void          StyleColorsClassic(ImGuiStyle* dst = NULL); // classic imgui style\n\n    // Windows\n    // - Begin() = push window to the stack and start appending to it. End() = pop window from the stack.\n    // - Passing 'bool* p_open != NULL' shows a window-closing widget in the upper-right corner of the window,\n    //   which clicking will set the boolean to false when clicked.\n    // - You may append multiple times to the same window during the same frame by calling Begin()/End() pairs multiple times.\n    //   Some information such as 'flags' or 'p_open' will only be considered by the first call to Begin().\n    // - Begin() return false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting\n    //   anything to the window. Always call a matching End() for each Begin() call, regardless of its return value!\n    //   [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions\n    //    such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding\n    //    BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.]\n    // - Note that the bottom of window stack always contains a window called \"Debug\".\n    IMGUI_API bool          Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0);\n    IMGUI_API void          End();\n\n    // Child Windows\n    // - Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window. Child windows can embed their own child.\n    // - Before 1.90 (November 2023), the \"ImGuiChildFlags child_flags = 0\" parameter was \"bool border = false\".\n    //   This API is backward compatible with old code, as we guarantee that ImGuiChildFlags_Border == true.\n    //   Consider updating your old call sites:\n    //      BeginChild(\"Name\", size, false)   -> Begin(\"Name\", size, 0); or Begin(\"Name\", size, ImGuiChildFlags_None);\n    //      BeginChild(\"Name\", size, true)    -> Begin(\"Name\", size, ImGuiChildFlags_Border);\n    // - Manual sizing (each axis can use a different setting e.g. ImVec2(0.0f, 400.0f)):\n    //     == 0.0f: use remaining parent window size for this axis.\n    //      > 0.0f: use specified size for this axis.\n    //      < 0.0f: right/bottom-align to specified distance from available content boundaries.\n    // - Specifying ImGuiChildFlags_AutoResizeX or ImGuiChildFlags_AutoResizeY makes the sizing automatic based on child contents.\n    //   Combining both ImGuiChildFlags_AutoResizeX _and_ ImGuiChildFlags_AutoResizeY defeats purpose of a scrolling region and is NOT recommended.\n    // - BeginChild() returns false to indicate the window is collapsed or fully clipped, so you may early out and omit submitting\n    //   anything to the window. Always call a matching EndChild() for each BeginChild() call, regardless of its return value.\n    //   [Important: due to legacy reason, Begin/End and BeginChild/EndChild are inconsistent with all other functions\n    //    such as BeginMenu/EndMenu, BeginPopup/EndPopup, etc. where the EndXXX call should only be called if the corresponding\n    //    BeginXXX function returned true. Begin and BeginChild are the only odd ones out. Will be fixed in a future update.]\n    IMGUI_API bool          BeginChild(const char* str_id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0);\n    IMGUI_API bool          BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiChildFlags child_flags = 0, ImGuiWindowFlags window_flags = 0);\n    IMGUI_API void          EndChild();\n\n    // Windows Utilities\n    // - 'current window' = the window we are appending into while inside a Begin()/End() block. 'next window' = next window we will Begin() into.\n    IMGUI_API bool          IsWindowAppearing();\n    IMGUI_API bool          IsWindowCollapsed();\n    IMGUI_API bool          IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options.\n    IMGUI_API bool          IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options. IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! Refer to FAQ entry \"How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?\" for details.\n    IMGUI_API ImDrawList*   GetWindowDrawList();                        // get draw list associated to the current window, to append your own drawing primitives\n    IMGUI_API ImVec2        GetWindowPos();                             // get current window position in screen space (note: it is unlikely you need to use this. Consider using current layout pos instead, GetCursorScreenPos())\n    IMGUI_API ImVec2        GetWindowSize();                            // get current window size (note: it is unlikely you need to use this. Consider using GetCursorScreenPos() and e.g. GetContentRegionAvail() instead)\n    IMGUI_API float         GetWindowWidth();                           // get current window width (shortcut for GetWindowSize().x)\n    IMGUI_API float         GetWindowHeight();                          // get current window height (shortcut for GetWindowSize().y)\n\n    // Window manipulation\n    // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin).\n    IMGUI_API void          SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0, 0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc.\n    IMGUI_API void          SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0);                  // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin()\n    IMGUI_API void          SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use 0.0f or FLT_MAX if you don't want limits. Use -1 for both min and max of same axis to preserve current size (which itself is a constraint). Use callback to apply non-trivial programmatic constraints.\n    IMGUI_API void          SetNextWindowContentSize(const ImVec2& size);                               // set next window content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding. set an axis to 0.0f to leave it automatic. call before Begin()\n    IMGUI_API void          SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0);                 // set next window collapsed state. call before Begin()\n    IMGUI_API void          SetNextWindowFocus();                                                       // set next window to be focused / top-most. call before Begin()\n    IMGUI_API void          SetNextWindowScroll(const ImVec2& scroll);                                  // set next window scrolling value (use < 0.0f to not affect a given axis).\n    IMGUI_API void          SetNextWindowBgAlpha(float alpha);                                          // set next window background color alpha. helper to easily override the Alpha component of ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground.\n    IMGUI_API void          SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0);                        // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects.\n    IMGUI_API void          SetWindowSize(const ImVec2& size, ImGuiCond cond = 0);                      // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0, 0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects.\n    IMGUI_API void          SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0);                     // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed().\n    IMGUI_API void          SetWindowFocus();                                                           // (not recommended) set current window to be focused / top-most. prefer using SetNextWindowFocus().\n    IMGUI_API void          SetWindowFontScale(float scale);                                            // [OBSOLETE] set font scale. Adjust IO.FontGlobalScale if you want to scale all windows. This is an old API! For correct scaling, prefer to reload font + rebuild ImFontAtlas + call style.ScaleAllSizes().\n    IMGUI_API void          SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0);      // set named window position.\n    IMGUI_API void          SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0);    // set named window size. set axis to 0.0f to force an auto-fit on this axis.\n    IMGUI_API void          SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0);   // set named window collapsed state\n    IMGUI_API void          SetWindowFocus(const char* name);                                           // set named window to be focused / top-most. use NULL to remove focus.\n\n    // Content region\n    // - Retrieve available space from a given point. GetContentRegionAvail() is frequently useful.\n    // - Those functions are bound to be redesigned (they are confusing, incomplete and the Min/Max return values are in local window coordinates which increases confusion)\n    IMGUI_API ImVec2        GetContentRegionAvail();                                        // == GetContentRegionMax() - GetCursorPos()\n    IMGUI_API ImVec2        GetContentRegionMax();                                          // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates\n    IMGUI_API ImVec2        GetWindowContentRegionMin();                                    // content boundaries min for the full window (roughly (0,0)-Scroll), in window coordinates\n    IMGUI_API ImVec2        GetWindowContentRegionMax();                                    // content boundaries max for the full window (roughly (0,0)+Size-Scroll) where Size can be overridden with SetNextWindowContentSize(), in window coordinates\n\n    // Windows Scrolling\n    // - Any change of Scroll will be applied at the beginning of next frame in the first call to Begin().\n    // - You may instead use SetNextWindowScroll() prior to calling Begin() to avoid this delay, as an alternative to using SetScrollX()/SetScrollY().\n    IMGUI_API float         GetScrollX();                                                   // get scrolling amount [0 .. GetScrollMaxX()]\n    IMGUI_API float         GetScrollY();                                                   // get scrolling amount [0 .. GetScrollMaxY()]\n    IMGUI_API void          SetScrollX(float scroll_x);                                     // set scrolling amount [0 .. GetScrollMaxX()]\n    IMGUI_API void          SetScrollY(float scroll_y);                                     // set scrolling amount [0 .. GetScrollMaxY()]\n    IMGUI_API float         GetScrollMaxX();                                                // get maximum scrolling amount ~~ ContentSize.x - WindowSize.x - DecorationsSize.x\n    IMGUI_API float         GetScrollMaxY();                                                // get maximum scrolling amount ~~ ContentSize.y - WindowSize.y - DecorationsSize.y\n    IMGUI_API void          SetScrollHereX(float center_x_ratio = 0.5f);                    // adjust scrolling amount to make current cursor position visible. center_x_ratio=0.0: left, 0.5: center, 1.0: right. When using to make a \"default/current item\" visible, consider using SetItemDefaultFocus() instead.\n    IMGUI_API void          SetScrollHereY(float center_y_ratio = 0.5f);                    // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a \"default/current item\" visible, consider using SetItemDefaultFocus() instead.\n    IMGUI_API void          SetScrollFromPosX(float local_x, float center_x_ratio = 0.5f);  // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position.\n    IMGUI_API void          SetScrollFromPosY(float local_y, float center_y_ratio = 0.5f);  // adjust scrolling amount to make given position visible. Generally GetCursorStartPos() + offset to compute a valid position.\n\n    // Parameters stacks (shared)\n    IMGUI_API void          PushFont(ImFont* font);                                         // use NULL as a shortcut to push default font\n    IMGUI_API void          PopFont();\n    IMGUI_API void          PushStyleColor(ImGuiCol idx, ImU32 col);                        // modify a style color. always use this if you modify the style after NewFrame().\n    IMGUI_API void          PushStyleColor(ImGuiCol idx, const ImVec4& col);\n    IMGUI_API void          PopStyleColor(int count = 1);\n    IMGUI_API void          PushStyleVar(ImGuiStyleVar idx, float val);                     // modify a style float variable. always use this if you modify the style after NewFrame().\n    IMGUI_API void          PushStyleVar(ImGuiStyleVar idx, const ImVec2& val);             // modify a style ImVec2 variable. always use this if you modify the style after NewFrame().\n    IMGUI_API void          PopStyleVar(int count = 1);\n    IMGUI_API void          PushTabStop(bool tab_stop);                                     // == tab stop enable. Allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets\n    IMGUI_API void          PopTabStop();\n    IMGUI_API void          PushButtonRepeat(bool repeat);                                  // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame.\n    IMGUI_API void          PopButtonRepeat();\n\n    // Parameters stacks (current window)\n    IMGUI_API void          PushItemWidth(float item_width);                                // push width of items for common large \"item+label\" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side).\n    IMGUI_API void          PopItemWidth();\n    IMGUI_API void          SetNextItemWidth(float item_width);                             // set width of the _next_ common large \"item+label\" widget. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -FLT_MIN always align width to the right side)\n    IMGUI_API float         CalcItemWidth();                                                // width of item given pushed settings and current cursor position. NOT necessarily the width of last item unlike most 'Item' functions.\n    IMGUI_API void          PushTextWrapPos(float wrap_local_pos_x = 0.0f);                 // push word-wrapping position for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space\n    IMGUI_API void          PopTextWrapPos();\n\n    // Style read access\n    // - Use the ShowStyleEditor() function to interactively see/edit the colors.\n    IMGUI_API ImFont*       GetFont();                                                      // get current font\n    IMGUI_API float         GetFontSize();                                                  // get current font size (= height in pixels) of current font with current scale applied\n    IMGUI_API ImVec2        GetFontTexUvWhitePixel();                                       // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API\n    IMGUI_API ImU32         GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f);              // retrieve given style color with style alpha applied and optional extra alpha multiplier, packed as a 32-bit value suitable for ImDrawList\n    IMGUI_API ImU32         GetColorU32(const ImVec4& col);                                 // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList\n    IMGUI_API ImU32         GetColorU32(ImU32 col);                                         // retrieve given color with style alpha applied, packed as a 32-bit value suitable for ImDrawList\n    IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx);                                // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in.\n\n    // Layout cursor positioning\n    // - By \"cursor\" we mean the current output position.\n    // - The typical widget behavior is to output themselves at the current cursor position, then move the cursor one line down.\n    // - You can call SameLine() between widgets to undo the last carriage return and output at the right of the preceding widget.\n    // - Attention! We currently have inconsistencies between window-local and absolute positions we will aim to fix with future API:\n    //    - Absolute coordinate:        GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. -> this is the preferred way forward.\n    //    - Window-local coordinates:   SameLine(), GetCursorPos(), SetCursorPos(), GetCursorStartPos(), GetContentRegionMax(), GetWindowContentRegion*(), PushTextWrapPos()\n    // - GetCursorScreenPos() = GetCursorPos() + GetWindowPos(). GetWindowPos() is almost only ever useful to convert from window-local to absolute coordinates.\n    IMGUI_API ImVec2        GetCursorScreenPos();                                           // cursor position in absolute coordinates (prefer using this, also more useful to work with ImDrawList API).\n    IMGUI_API void          SetCursorScreenPos(const ImVec2& pos);                          // cursor position in absolute coordinates\n    IMGUI_API ImVec2        GetCursorPos();                                                 // [window-local] cursor position in window coordinates (relative to window position)\n    IMGUI_API float         GetCursorPosX();                                                // [window-local] \"\n    IMGUI_API float         GetCursorPosY();                                                // [window-local] \"\n    IMGUI_API void          SetCursorPos(const ImVec2& local_pos);                          // [window-local] \"\n    IMGUI_API void          SetCursorPosX(float local_x);                                   // [window-local] \"\n    IMGUI_API void          SetCursorPosY(float local_y);                                   // [window-local] \"\n    IMGUI_API ImVec2        GetCursorStartPos();                                            // [window-local] initial cursor position, in window coordinates\n\n    // Other layout functions\n    IMGUI_API void          Separator();                                                    // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator.\n    IMGUI_API void          SameLine(float offset_from_start_x=0.0f, float spacing=-1.0f);  // call between widgets or groups to layout them horizontally. X position given in window coordinates.\n    IMGUI_API void          NewLine();                                                      // undo a SameLine() or force a new line when in a horizontal-layout context.\n    IMGUI_API void          Spacing();                                                      // add vertical spacing.\n    IMGUI_API void          Dummy(const ImVec2& size);                                      // add a dummy item of given size. unlike InvisibleButton(), Dummy() won't take the mouse click or be navigable into.\n    IMGUI_API void          Indent(float indent_w = 0.0f);                                  // move content position toward the right, by indent_w, or style.IndentSpacing if indent_w <= 0\n    IMGUI_API void          Unindent(float indent_w = 0.0f);                                // move content position back to the left, by indent_w, or style.IndentSpacing if indent_w <= 0\n    IMGUI_API void          BeginGroup();                                                   // lock horizontal starting position\n    IMGUI_API void          EndGroup();                                                     // unlock horizontal starting position + capture the whole group bounding box into one \"item\" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.)\n    IMGUI_API void          AlignTextToFramePadding();                                      // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item)\n    IMGUI_API float         GetTextLineHeight();                                            // ~ FontSize\n    IMGUI_API float         GetTextLineHeightWithSpacing();                                 // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text)\n    IMGUI_API float         GetFrameHeight();                                               // ~ FontSize + style.FramePadding.y * 2\n    IMGUI_API float         GetFrameHeightWithSpacing();                                    // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets)\n\n    // ID stack/scopes\n    // Read the FAQ (docs/FAQ.md or http://dearimgui.com/faq) for more details about how ID are handled in dear imgui.\n    // - Those questions are answered and impacted by understanding of the ID stack system:\n    //   - \"Q: Why is my widget not reacting when I click on it?\"\n    //   - \"Q: How can I have widgets with an empty label?\"\n    //   - \"Q: How can I have multiple widgets with the same label?\"\n    // - Short version: ID are hashes of the entire ID stack. If you are creating widgets in a loop you most likely\n    //   want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them.\n    // - You can also use the \"Label##foobar\" syntax within widget label to distinguish them from each others.\n    // - In this header file we use the \"label\"/\"name\" terminology to denote a string that will be displayed + used as an ID,\n    //   whereas \"str_id\" denote a string that is only used as an ID and not normally displayed.\n    IMGUI_API void          PushID(const char* str_id);                                     // push string into the ID stack (will hash string).\n    IMGUI_API void          PushID(const char* str_id_begin, const char* str_id_end);       // push string into the ID stack (will hash string).\n    IMGUI_API void          PushID(const void* ptr_id);                                     // push pointer into the ID stack (will hash pointer).\n    IMGUI_API void          PushID(int int_id);                                             // push integer into the ID stack (will hash integer).\n    IMGUI_API void          PopID();                                                        // pop from the ID stack.\n    IMGUI_API ImGuiID       GetID(const char* str_id);                                      // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself\n    IMGUI_API ImGuiID       GetID(const char* str_id_begin, const char* str_id_end);\n    IMGUI_API ImGuiID       GetID(const void* ptr_id);\n\n    // Widgets: Text\n    IMGUI_API void          TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text(\"%s\", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text.\n    IMGUI_API void          Text(const char* fmt, ...)                                      IM_FMTARGS(1); // formatted text\n    IMGUI_API void          TextV(const char* fmt, va_list args)                            IM_FMTLIST(1);\n    IMGUI_API void          TextColored(const ImVec4& col, const char* fmt, ...)            IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor();\n    IMGUI_API void          TextColoredV(const ImVec4& col, const char* fmt, va_list args)  IM_FMTLIST(2);\n    IMGUI_API void          TextDisabled(const char* fmt, ...)                              IM_FMTARGS(1); // shortcut for PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); Text(fmt, ...); PopStyleColor();\n    IMGUI_API void          TextDisabledV(const char* fmt, va_list args)                    IM_FMTLIST(1);\n    IMGUI_API void          TextWrapped(const char* fmt, ...)                               IM_FMTARGS(1); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();. Note that this won't work on an auto-resizing window if there's no other widgets to extend the window width, yoy may need to set a size using SetNextWindowSize().\n    IMGUI_API void          TextWrappedV(const char* fmt, va_list args)                     IM_FMTLIST(1);\n    IMGUI_API void          LabelText(const char* label, const char* fmt, ...)              IM_FMTARGS(2); // display text+label aligned the same way as value+label widgets\n    IMGUI_API void          LabelTextV(const char* label, const char* fmt, va_list args)    IM_FMTLIST(2);\n    IMGUI_API void          BulletText(const char* fmt, ...)                                IM_FMTARGS(1); // shortcut for Bullet()+Text()\n    IMGUI_API void          BulletTextV(const char* fmt, va_list args)                      IM_FMTLIST(1);\n    IMGUI_API void          SeparatorText(const char* label);                               // currently: formatted text with an horizontal line\n\n    // Widgets: Main\n    // - Most widgets return true when the value has been changed or when pressed/selected\n    // - You may also use one of the many IsItemXXX functions (e.g. IsItemActive, IsItemHovered, etc.) to query widget state.\n    IMGUI_API bool          Button(const char* label, const ImVec2& size = ImVec2(0, 0));   // button\n    IMGUI_API bool          SmallButton(const char* label);                                 // button with (FramePadding.y == 0) to easily embed within text\n    IMGUI_API bool          InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.)\n    IMGUI_API bool          ArrowButton(const char* str_id, ImGuiDir dir);                  // square button with an arrow shape\n    IMGUI_API bool          Checkbox(const char* label, bool* v);\n    IMGUI_API bool          CheckboxFlags(const char* label, int* flags, int flags_value);\n    IMGUI_API bool          CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value);\n    IMGUI_API bool          RadioButton(const char* label, bool active);                    // use with e.g. if (RadioButton(\"one\", my_value==1)) { my_value = 1; }\n    IMGUI_API bool          RadioButton(const char* label, int* v, int v_button);           // shortcut to handle the above pattern when value is an integer\n    IMGUI_API void          ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-FLT_MIN, 0), const char* overlay = NULL);\n    IMGUI_API void          Bullet();                                                       // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses\n\n    // Widgets: Images\n    // - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples\n    // - Note that ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.\n    IMGUI_API void          Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0));\n    IMGUI_API bool          ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1));\n\n    // Widgets: Combo Box (Dropdown)\n    // - The BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items.\n    // - The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. This is analogous to how ListBox are created.\n    IMGUI_API bool          BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0);\n    IMGUI_API void          EndCombo(); // only call EndCombo() if BeginCombo() returns true!\n    IMGUI_API bool          Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1);\n    IMGUI_API bool          Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1);      // Separate items with \\0 within a string, end item-list with \\0\\0. e.g. \"One\\0Two\\0Three\\0\"\n    IMGUI_API bool          Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items = -1);\n\n    // Widgets: Drag Sliders\n    // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.\n    // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every function, note that a 'float v[X]' function argument is the same as 'float* v',\n    //   the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x\n    // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. \"%.3f\" -> 1.234; \"%5.2f secs\" -> 01.23 secs; \"Biscuit: %.0f\" -> Biscuit: 1; etc.\n    // - Format string may also be set to NULL or use the default format (\"%f\" or \"%d\").\n    // - Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision).\n    // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used.\n    // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum.\n    // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.\n    // - Legacy: Pre-1.78 there are DragXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.\n    //   If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361\n    IMGUI_API bool          DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);     // If v_min >= v_max we have no bound\n    IMGUI_API bool          DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = \"%.3f\", const char* format_max = NULL, ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = \"%d\", ImGuiSliderFlags flags = 0);  // If v_min >= v_max we have no bound\n    IMGUI_API bool          DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = \"%d\", const char* format_max = NULL, ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed = 1.0f, const void* p_min = NULL, const void* p_max = NULL, const char* format = NULL, ImGuiSliderFlags flags = 0);\n\n    // Widgets: Regular Sliders\n    // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp.\n    // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. \"%.3f\" -> 1.234; \"%5.2f secs\" -> 01.23 secs; \"Biscuit: %.0f\" -> Biscuit: 1; etc.\n    // - Format string may also be set to NULL or use the default format (\"%f\" or \"%d\").\n    // - Legacy: Pre-1.78 there are SliderXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument.\n    //   If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361\n    IMGUI_API bool          SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);     // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display.\n    IMGUI_API bool          SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f, const char* format = \"%.0f deg\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = \"%.3f\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = \"%d\", ImGuiSliderFlags flags = 0);\n    IMGUI_API bool          VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format = NULL, ImGuiSliderFlags flags = 0);\n\n    // Widgets: Input with Keyboard\n    // - If you want to use InputText() with std::string or any custom dynamic string type, see misc/cpp/imgui_stdlib.h and comments in imgui_demo.cpp.\n    // - Most of the ImGuiInputTextFlags flags are only useful for InputText() and not for InputFloatX, InputIntX, InputDouble etc.\n    IMGUI_API bool          InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);\n    IMGUI_API bool          InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);\n    IMGUI_API bool          InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);\n    IMGUI_API bool          InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = \"%.3f\", ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputFloat2(const char* label, float v[2], const char* format = \"%.3f\", ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputFloat3(const char* label, float v[3], const char* format = \"%.3f\", ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputFloat4(const char* label, float v[4], const char* format = \"%.3f\", ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = \"%.6f\", ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0);\n    IMGUI_API bool          InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step = NULL, const void* p_step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags flags = 0);\n\n    // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little color square that can be left-clicked to open a picker, and right-clicked to open an option menu.)\n    // - Note that in C++ a 'float v[X]' function argument is the _same_ as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible.\n    // - You can pass the address of a first float element out of a contiguous structure, e.g. &myvector.x\n    IMGUI_API bool          ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);\n    IMGUI_API bool          ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0);\n    IMGUI_API bool          ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0);\n    IMGUI_API bool          ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL);\n    IMGUI_API bool          ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // display a color square/button, hover for details, return true when pressed.\n    IMGUI_API void          SetColorEditOptions(ImGuiColorEditFlags flags);                     // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls.\n\n    // Widgets: Trees\n    // - TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents.\n    IMGUI_API bool          TreeNode(const char* label);\n    IMGUI_API bool          TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2);   // helper variation to easily decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet().\n    IMGUI_API bool          TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2);   // \"\n    IMGUI_API bool          TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2);\n    IMGUI_API bool          TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2);\n    IMGUI_API bool          TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags = 0);\n    IMGUI_API bool          TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3);\n    IMGUI_API bool          TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3);\n    IMGUI_API bool          TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3);\n    IMGUI_API bool          TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3);\n    IMGUI_API void          TreePush(const char* str_id);                                       // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired.\n    IMGUI_API void          TreePush(const void* ptr_id);                                       // \"\n    IMGUI_API void          TreePop();                                                          // ~ Unindent()+PopId()\n    IMGUI_API float         GetTreeNodeToLabelSpacing();                                        // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode\n    IMGUI_API bool          CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0);  // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop().\n    IMGUI_API bool          CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags = 0); // when 'p_visible != NULL': if '*p_visible==true' display an additional small close button on upper right of the header which will set the bool to false when clicked, if '*p_visible==false' don't display the header.\n    IMGUI_API void          SetNextItemOpen(bool is_open, ImGuiCond cond = 0);                  // set next TreeNode/CollapsingHeader open state.\n\n    // Widgets: Selectables\n    // - A selectable highlights when hovered, and can display another color when selected.\n    // - Neighbors selectable extend their highlight bounds in order to leave no gap between them. This is so a series of selected Selectable appear contiguous.\n    IMGUI_API bool          Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0)); // \"bool selected\" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height\n    IMGUI_API bool          Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0, 0));      // \"bool* p_selected\" point to the selection state (read-write), as a convenient helper.\n\n    // Widgets: List Boxes\n    // - This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.\n    // - You can submit contents and manage your selection state however you want it, by creating e.g. Selectable() or any other items.\n    // - The simplified/old ListBox() api are helpers over BeginListBox()/EndListBox() which are kept available for convenience purpose. This is analoguous to how Combos are created.\n    // - Choose frame width:   size.x > 0.0f: custom  /  size.x < 0.0f or -FLT_MIN: right-align   /  size.x = 0.0f (default): use current ItemWidth\n    // - Choose frame height:  size.y > 0.0f: custom  /  size.y < 0.0f or -FLT_MIN: bottom-align  /  size.y = 0.0f (default): arbitrary default height which can fit ~7 items\n    IMGUI_API bool          BeginListBox(const char* label, const ImVec2& size = ImVec2(0, 0)); // open a framed scrolling region\n    IMGUI_API void          EndListBox();                                                       // only call EndListBox() if BeginListBox() returned true!\n    IMGUI_API bool          ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1);\n    IMGUI_API bool          ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items = -1);\n\n    // Widgets: Data Plotting\n    // - Consider using ImPlot (https://github.com/epezent/implot) which is much better!\n    IMGUI_API void          PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));\n    IMGUI_API void          PlotLines(const char* label, float(*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0));\n    IMGUI_API void          PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));\n    IMGUI_API void          PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0));\n\n    // Widgets: Value() Helpers.\n    // - Those are merely shortcut to calling Text() with a format string. Output single value in \"name: value\" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace)\n    IMGUI_API void          Value(const char* prefix, bool b);\n    IMGUI_API void          Value(const char* prefix, int v);\n    IMGUI_API void          Value(const char* prefix, unsigned int v);\n    IMGUI_API void          Value(const char* prefix, float v, const char* float_format = NULL);\n\n    // Widgets: Menus\n    // - Use BeginMenuBar() on a window ImGuiWindowFlags_MenuBar to append to its menu bar.\n    // - Use BeginMainMenuBar() to create a menu bar at the top of the screen and append to it.\n    // - Use BeginMenu() to create a menu. You can call BeginMenu() multiple time with the same identifier to append more items to it.\n    // - Not that MenuItem() keyboardshortcuts are displayed as a convenience but _not processed_ by Dear ImGui at the moment.\n    IMGUI_API bool          BeginMenuBar();                                                     // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window).\n    IMGUI_API void          EndMenuBar();                                                       // only call EndMenuBar() if BeginMenuBar() returns true!\n    IMGUI_API bool          BeginMainMenuBar();                                                 // create and append to a full screen menu-bar.\n    IMGUI_API void          EndMainMenuBar();                                                   // only call EndMainMenuBar() if BeginMainMenuBar() returns true!\n    IMGUI_API bool          BeginMenu(const char* label, bool enabled = true);                  // create a sub-menu entry. only call EndMenu() if this returns true!\n    IMGUI_API void          EndMenu();                                                          // only call EndMenu() if BeginMenu() returns true!\n    IMGUI_API bool          MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true);  // return true when activated.\n    IMGUI_API bool          MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true);              // return true when activated + toggle (*p_selected) if p_selected != NULL\n\n    // Tooltips\n    // - Tooltips are windows following the mouse. They do not take focus away.\n    // - A tooltip window can contain items of any types. SetTooltip() is a shortcut for the 'if (BeginTooltip()) { Text(...); EndTooltip(); }' idiom.\n    IMGUI_API bool          BeginTooltip();                                                     // begin/append a tooltip window.\n    IMGUI_API void          EndTooltip();                                                       // only call EndTooltip() if BeginTooltip()/BeginItemTooltip() returns true!\n    IMGUI_API void          SetTooltip(const char* fmt, ...) IM_FMTARGS(1);                     // set a text-only tooltip. Often used after a ImGui::IsItemHovered() check. Override any previous call to SetTooltip().\n    IMGUI_API void          SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1);\n\n    // Tooltips: helpers for showing a tooltip when hovering an item\n    // - BeginItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip) && BeginTooltip())' idiom.\n    // - SetItemTooltip() is a shortcut for the 'if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) { SetTooltip(...); }' idiom.\n    // - Where 'ImGuiHoveredFlags_ForTooltip' itself is a shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on active input type. For mouse it defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'.\n    IMGUI_API bool          BeginItemTooltip();                                                 // begin/append a tooltip window if preceding item was hovered.\n    IMGUI_API void          SetItemTooltip(const char* fmt, ...) IM_FMTARGS(1);                 // set a text-only tooltip if preceeding item was hovered. override any previous call to SetTooltip().\n    IMGUI_API void          SetItemTooltipV(const char* fmt, va_list args) IM_FMTLIST(1);\n\n    // Popups, Modals\n    //  - They block normal mouse hovering detection (and therefore most mouse interactions) behind them.\n    //  - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE.\n    //  - Their visibility state (~bool) is held internally instead of being held by the programmer as we are used to with regular Begin*() calls.\n    //  - The 3 properties above are related: we need to retain popup visibility state in the library because popups may be closed as any time.\n    //  - You can bypass the hovering restriction by using ImGuiHoveredFlags_AllowWhenBlockedByPopup when calling IsItemHovered() or IsWindowHovered().\n    //  - IMPORTANT: Popup identifiers are relative to the current ID stack, so OpenPopup and BeginPopup generally needs to be at the same level of the stack.\n    //    This is sometimes leading to confusing mistakes. May rework this in the future.\n\n    // Popups: begin/end functions\n    //  - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards. ImGuiWindowFlags are forwarded to the window.\n    //  - BeginPopupModal(): block every interaction behind the window, cannot be closed by user, add a dimming background, has a title bar.\n    IMGUI_API bool          BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0);                         // return true if the popup is open, and you can start outputting to it.\n    IMGUI_API bool          BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it.\n    IMGUI_API void          EndPopup();                                                                         // only call EndPopup() if BeginPopupXXX() returns true!\n\n    // Popups: open/close functions\n    //  - OpenPopup(): set popup state to open. ImGuiPopupFlags are available for opening options.\n    //  - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE.\n    //  - CloseCurrentPopup(): use inside the BeginPopup()/EndPopup() scope to close manually.\n    //  - CloseCurrentPopup() is called by default by Selectable()/MenuItem() when activated (FIXME: need some options).\n    //  - Use ImGuiPopupFlags_NoOpenOverExistingPopup to avoid opening a popup if there's already one at the same level. This is equivalent to e.g. testing for !IsAnyPopupOpen() prior to OpenPopup().\n    //  - Use IsWindowAppearing() after BeginPopup() to tell if a window just opened.\n    //  - IMPORTANT: Notice that for OpenPopupOnItemClick() we exceptionally default flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter\n    IMGUI_API void          OpenPopup(const char* str_id, ImGuiPopupFlags popup_flags = 0);                     // call to mark popup as open (don't call every frame!).\n    IMGUI_API void          OpenPopup(ImGuiID id, ImGuiPopupFlags popup_flags = 0);                             // id overload to facilitate calling from nested stacks\n    IMGUI_API void          OpenPopupOnItemClick(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);   // helper to open popup when clicked on last item. Default to ImGuiPopupFlags_MouseButtonRight == 1. (note: actually triggers on the mouse _released_ event to be consistent with popup behaviors)\n    IMGUI_API void          CloseCurrentPopup();                                                                // manually close the popup we have begin-ed into.\n\n    // Popups: open+begin combined functions helpers\n    //  - Helpers to do OpenPopup+BeginPopup where the Open action is triggered by e.g. hovering an item and right-clicking.\n    //  - They are convenient to easily create context menus, hence the name.\n    //  - IMPORTANT: Notice that BeginPopupContextXXX takes ImGuiPopupFlags just like OpenPopup() and unlike BeginPopup(). For full consistency, we may add ImGuiWindowFlags to the BeginPopupContextXXX functions in the future.\n    //  - IMPORTANT: Notice that we exceptionally default their flags to 1 (== ImGuiPopupFlags_MouseButtonRight) for backward compatibility with older API taking 'int mouse_button = 1' parameter, so if you add other flags remember to re-add the ImGuiPopupFlags_MouseButtonRight.\n    IMGUI_API bool          BeginPopupContextItem(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);  // open+begin popup when clicked on last item. Use str_id==NULL to associate the popup to previous item. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp!\n    IMGUI_API bool          BeginPopupContextWindow(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);// open+begin popup when clicked on current window.\n    IMGUI_API bool          BeginPopupContextVoid(const char* str_id = NULL, ImGuiPopupFlags popup_flags = 1);  // open+begin popup when clicked in void (where there are no windows).\n\n    // Popups: query functions\n    //  - IsPopupOpen(): return true if the popup is open at the current BeginPopup() level of the popup stack.\n    //  - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId: return true if any popup is open at the current BeginPopup() level of the popup stack.\n    //  - IsPopupOpen() with ImGuiPopupFlags_AnyPopupId + ImGuiPopupFlags_AnyPopupLevel: return true if any popup is open.\n    IMGUI_API bool          IsPopupOpen(const char* str_id, ImGuiPopupFlags flags = 0);                         // return true if the popup is open.\n\n    // Tables\n    // - Full-featured replacement for old Columns API.\n    // - See Demo->Tables for demo code. See top of imgui_tables.cpp for general commentary.\n    // - See ImGuiTableFlags_ and ImGuiTableColumnFlags_ enums for a description of available flags.\n    // The typical call flow is:\n    // - 1. Call BeginTable(), early out if returning false.\n    // - 2. Optionally call TableSetupColumn() to submit column name/flags/defaults.\n    // - 3. Optionally call TableSetupScrollFreeze() to request scroll freezing of columns/rows.\n    // - 4. Optionally call TableHeadersRow() to submit a header row. Names are pulled from TableSetupColumn() data.\n    // - 5. Populate contents:\n    //    - In most situations you can use TableNextRow() + TableSetColumnIndex(N) to start appending into a column.\n    //    - If you are using tables as a sort of grid, where every column is holding the same type of contents,\n    //      you may prefer using TableNextColumn() instead of TableNextRow() + TableSetColumnIndex().\n    //      TableNextColumn() will automatically wrap-around into the next row if needed.\n    //    - IMPORTANT: Comparatively to the old Columns() API, we need to call TableNextColumn() for the first column!\n    //    - Summary of possible call flow:\n    //        --------------------------------------------------------------------------------------------------------\n    //        TableNextRow() -> TableSetColumnIndex(0) -> Text(\"Hello 0\") -> TableSetColumnIndex(1) -> Text(\"Hello 1\")  // OK\n    //        TableNextRow() -> TableNextColumn()      -> Text(\"Hello 0\") -> TableNextColumn()      -> Text(\"Hello 1\")  // OK\n    //                          TableNextColumn()      -> Text(\"Hello 0\") -> TableNextColumn()      -> Text(\"Hello 1\")  // OK: TableNextColumn() automatically gets to next row!\n    //        TableNextRow()                           -> Text(\"Hello 0\")                                               // Not OK! Missing TableSetColumnIndex() or TableNextColumn()! Text will not appear!\n    //        --------------------------------------------------------------------------------------------------------\n    // - 5. Call EndTable()\n    IMGUI_API bool          BeginTable(const char* str_id, int column, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0.0f, 0.0f), float inner_width = 0.0f);\n    IMGUI_API void          EndTable();                                         // only call EndTable() if BeginTable() returns true!\n    IMGUI_API void          TableNextRow(ImGuiTableRowFlags row_flags = 0, float min_row_height = 0.0f); // append into the first cell of a new row.\n    IMGUI_API bool          TableNextColumn();                                  // append into the next column (or first column of next row if currently in last column). Return true when column is visible.\n    IMGUI_API bool          TableSetColumnIndex(int column_n);                  // append into the specified column. Return true when column is visible.\n\n    // Tables: Headers & Columns declaration\n    // - Use TableSetupColumn() to specify label, resizing policy, default width/weight, id, various other flags etc.\n    // - Use TableHeadersRow() to create a header row and automatically submit a TableHeader() for each column.\n    //   Headers are required to perform: reordering, sorting, and opening the context menu.\n    //   The context menu can also be made available in columns body using ImGuiTableFlags_ContextMenuInBody.\n    // - You may manually submit headers using TableNextRow() + TableHeader() calls, but this is only useful in\n    //   some advanced use cases (e.g. adding custom widgets in header row).\n    // - Use TableSetupScrollFreeze() to lock columns/rows so they stay visible when scrolled.\n    IMGUI_API void          TableSetupColumn(const char* label, ImGuiTableColumnFlags flags = 0, float init_width_or_weight = 0.0f, ImGuiID user_id = 0);\n    IMGUI_API void          TableSetupScrollFreeze(int cols, int rows);         // lock columns/rows so they stay visible when scrolled.\n    IMGUI_API void          TableHeader(const char* label);                     // submit one header cell manually (rarely used)\n    IMGUI_API void          TableHeadersRow();                                  // submit a row with headers cells based on data provided to TableSetupColumn() + submit context menu\n    IMGUI_API void          TableAngledHeadersRow();                            // submit a row with angled headers for every column with the ImGuiTableColumnFlags_AngledHeader flag. MUST BE FIRST ROW.\n\n    // Tables: Sorting & Miscellaneous functions\n    // - Sorting: call TableGetSortSpecs() to retrieve latest sort specs for the table. NULL when not sorting.\n    //   When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have\n    //   changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting,\n    //   else you may wastefully sort your data every frame!\n    // - Functions args 'int column_n' treat the default value of -1 as the same as passing the current column index.\n    IMGUI_API ImGuiTableSortSpecs*  TableGetSortSpecs();                        // get latest sort specs for the table (NULL if not sorting).  Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable().\n    IMGUI_API int                   TableGetColumnCount();                      // return number of columns (value passed to BeginTable)\n    IMGUI_API int                   TableGetColumnIndex();                      // return current column index.\n    IMGUI_API int                   TableGetRowIndex();                         // return current row index.\n    IMGUI_API const char*           TableGetColumnName(int column_n = -1);      // return \"\" if column didn't have a name declared by TableSetupColumn(). Pass -1 to use current column.\n    IMGUI_API ImGuiTableColumnFlags TableGetColumnFlags(int column_n = -1);     // return column flags so you can query their Enabled/Visible/Sorted/Hovered status flags. Pass -1 to use current column.\n    IMGUI_API void                  TableSetColumnEnabled(int column_n, bool v);// change user accessible enabled/disabled state of a column. Set to false to hide the column. User can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody)\n    IMGUI_API void                  TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n = -1);  // change the color of a cell, row, or column. See ImGuiTableBgTarget_ flags for details.\n\n    // Legacy Columns API (prefer using Tables!)\n    // - You can also use SameLine(pos_x) to mimic simplified columns.\n    IMGUI_API void          Columns(int count = 1, const char* id = NULL, bool border = true);\n    IMGUI_API void          NextColumn();                                                       // next column, defaults to current row or next row if the current row is finished\n    IMGUI_API int           GetColumnIndex();                                                   // get current column index\n    IMGUI_API float         GetColumnWidth(int column_index = -1);                              // get column width (in pixels). pass -1 to use current column\n    IMGUI_API void          SetColumnWidth(int column_index, float width);                      // set column width (in pixels). pass -1 to use current column\n    IMGUI_API float         GetColumnOffset(int column_index = -1);                             // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetColumnsCount() inclusive. column 0 is typically 0.0f\n    IMGUI_API void          SetColumnOffset(int column_index, float offset_x);                  // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column\n    IMGUI_API int           GetColumnsCount();\n\n    // Tab Bars, Tabs\n    // - Note: Tabs are automatically created by the docking system (when in 'docking' branch). Use this to create tab bars/tabs yourself.\n    IMGUI_API bool          BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0);        // create and append into a TabBar\n    IMGUI_API void          EndTabBar();                                                        // only call EndTabBar() if BeginTabBar() returns true!\n    IMGUI_API bool          BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0); // create a Tab. Returns true if the Tab is selected.\n    IMGUI_API void          EndTabItem();                                                       // only call EndTabItem() if BeginTabItem() returns true!\n    IMGUI_API bool          TabItemButton(const char* label, ImGuiTabItemFlags flags = 0);      // create a Tab behaving like a button. return true when clicked. cannot be selected in the tab bar.\n    IMGUI_API void          SetTabItemClosed(const char* tab_or_docked_window_label);           // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name.\n\n    // Logging/Capture\n    // - All text output from the interface can be captured into tty/file/clipboard. By default, tree nodes are automatically opened during logging.\n    IMGUI_API void          LogToTTY(int auto_open_depth = -1);                                 // start logging to tty (stdout)\n    IMGUI_API void          LogToFile(int auto_open_depth = -1, const char* filename = NULL);   // start logging to file\n    IMGUI_API void          LogToClipboard(int auto_open_depth = -1);                           // start logging to OS clipboard\n    IMGUI_API void          LogFinish();                                                        // stop logging (close file, etc.)\n    IMGUI_API void          LogButtons();                                                       // helper to display buttons for logging to tty/file/clipboard\n    IMGUI_API void          LogText(const char* fmt, ...) IM_FMTARGS(1);                        // pass text data straight to log (without being displayed)\n    IMGUI_API void          LogTextV(const char* fmt, va_list args) IM_FMTLIST(1);\n\n    // Drag and Drop\n    // - On source items, call BeginDragDropSource(), if it returns true also call SetDragDropPayload() + EndDragDropSource().\n    // - On target candidates, call BeginDragDropTarget(), if it returns true also call AcceptDragDropPayload() + EndDragDropTarget().\n    // - If you stop calling BeginDragDropSource() the payload is preserved however it won't have a preview tooltip (we currently display a fallback \"...\" tooltip, see #1725)\n    // - An item can be both drag source and drop target.\n    IMGUI_API bool          BeginDragDropSource(ImGuiDragDropFlags flags = 0);                                      // call after submitting an item which may be dragged. when this return true, you can call SetDragDropPayload() + EndDragDropSource()\n    IMGUI_API bool          SetDragDropPayload(const char* type, const void* data, size_t sz, ImGuiCond cond = 0);  // type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. Return true when payload has been accepted.\n    IMGUI_API void          EndDragDropSource();                                                                    // only call EndDragDropSource() if BeginDragDropSource() returns true!\n    IMGUI_API bool                  BeginDragDropTarget();                                                          // call after submitting an item that may receive a payload. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget()\n    IMGUI_API const ImGuiPayload*   AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0);          // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released.\n    IMGUI_API void                  EndDragDropTarget();                                                            // only call EndDragDropTarget() if BeginDragDropTarget() returns true!\n    IMGUI_API const ImGuiPayload*   GetDragDropPayload();                                                           // peek directly into the current payload from anywhere. returns NULL when drag and drop is finished or inactive. use ImGuiPayload::IsDataType() to test for the payload type.\n\n    // Disabling [BETA API]\n    // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors)\n    // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled)\n    // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it.\n    IMGUI_API void          BeginDisabled(bool disabled = true);\n    IMGUI_API void          EndDisabled();\n\n    // Clipping\n    // - Mouse hovering is affected by ImGui::PushClipRect() calls, unlike direct calls to ImDrawList::PushClipRect() which are render only.\n    IMGUI_API void          PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect);\n    IMGUI_API void          PopClipRect();\n\n    // Focus, Activation\n    // - Prefer using \"SetItemDefaultFocus()\" over \"if (IsWindowAppearing()) SetScrollHereY()\" when applicable to signify \"this is the default item\"\n    IMGUI_API void          SetItemDefaultFocus();                                              // make last item the default focused item of a window.\n    IMGUI_API void          SetKeyboardFocusHere(int offset = 0);                               // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget.\n\n    // Overlapping mode\n    IMGUI_API void          SetNextItemAllowOverlap();                                          // allow next item to be overlapped by a subsequent item. Useful with invisible buttons, selectable, treenode covering an area where subsequent items may need to be added. Note that both Selectable() and TreeNode() have dedicated flags doing this.\n\n    // Item/Widgets Utilities and Query Functions\n    // - Most of the functions are referring to the previous Item that has been submitted.\n    // - See Demo Window under \"Widgets->Querying Status\" for an interactive visualization of most of those functions.\n    IMGUI_API bool          IsItemHovered(ImGuiHoveredFlags flags = 0);                         // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options.\n    IMGUI_API bool          IsItemActive();                                                     // is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item. Items that don't interact will always return false)\n    IMGUI_API bool          IsItemFocused();                                                    // is the last item focused for keyboard/gamepad navigation?\n    IMGUI_API bool          IsItemClicked(ImGuiMouseButton mouse_button = 0);                   // is the last item hovered and mouse clicked on? (**)  == IsMouseClicked(mouse_button) && IsItemHovered()Important. (**) this is NOT equivalent to the behavior of e.g. Button(). Read comments in function definition.\n    IMGUI_API bool          IsItemVisible();                                                    // is the last item visible? (items may be out of sight because of clipping/scrolling)\n    IMGUI_API bool          IsItemEdited();                                                     // did the last item modify its underlying value this frame? or was pressed? This is generally the same as the \"bool\" return value of many widgets.\n    IMGUI_API bool          IsItemActivated();                                                  // was the last item just made active (item was previously inactive).\n    IMGUI_API bool          IsItemDeactivated();                                                // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that require continuous editing.\n    IMGUI_API bool          IsItemDeactivatedAfterEdit();                                       // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that require continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item).\n    IMGUI_API bool          IsItemToggledOpen();                                                // was the last item open state toggled? set by TreeNode().\n    IMGUI_API bool          IsAnyItemHovered();                                                 // is any item hovered?\n    IMGUI_API bool          IsAnyItemActive();                                                  // is any item active?\n    IMGUI_API bool          IsAnyItemFocused();                                                 // is any item focused?\n    IMGUI_API ImGuiID       GetItemID();                                                        // get ID of last item (~~ often same ImGui::GetID(label) beforehand)\n    IMGUI_API ImVec2        GetItemRectMin();                                                   // get upper-left bounding rectangle of the last item (screen space)\n    IMGUI_API ImVec2        GetItemRectMax();                                                   // get lower-right bounding rectangle of the last item (screen space)\n    IMGUI_API ImVec2        GetItemRectSize();                                                  // get size of last item\n\n    // Viewports\n    // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.\n    // - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports.\n    // - In the future we will extend this concept further to also represent Platform Monitor and support a \"no main platform window\" operation mode.\n    IMGUI_API ImGuiViewport* GetMainViewport();                                                 // return primary/default viewport. This can never be NULL.\n\n    // Background/Foreground Draw Lists\n    IMGUI_API ImDrawList*   GetBackgroundDrawList();                                            // this draw list will be the first rendered one. Useful to quickly draw shapes/text behind dear imgui contents.\n    IMGUI_API ImDrawList*   GetForegroundDrawList();                                            // this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents.\n\n    // Miscellaneous Utilities\n    IMGUI_API bool          IsRectVisible(const ImVec2& size);                                  // test if rectangle (of given size, starting from cursor position) is visible / not clipped.\n    IMGUI_API bool          IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max);      // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side.\n    IMGUI_API double        GetTime();                                                          // get global imgui time. incremented by io.DeltaTime every frame.\n    IMGUI_API int           GetFrameCount();                                                    // get global imgui frame count. incremented by 1 every frame.\n    IMGUI_API ImDrawListSharedData* GetDrawListSharedData();                                    // you may use this when creating your own ImDrawList instances.\n    IMGUI_API const char*   GetStyleColorName(ImGuiCol idx);                                    // get a string corresponding to the enum value (for display, saving, etc.).\n    IMGUI_API void          SetStateStorage(ImGuiStorage* storage);                             // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it)\n    IMGUI_API ImGuiStorage* GetStateStorage();\n\n    // Text Utilities\n    IMGUI_API ImVec2        CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f);\n\n    // Color Utilities\n    IMGUI_API ImVec4        ColorConvertU32ToFloat4(ImU32 in);\n    IMGUI_API ImU32         ColorConvertFloat4ToU32(const ImVec4& in);\n    IMGUI_API void          ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v);\n    IMGUI_API void          ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b);\n\n    // Inputs Utilities: Keyboard/Mouse/Gamepad\n    // - the ImGuiKey enum contains all possible keyboard, mouse and gamepad inputs (e.g. ImGuiKey_A, ImGuiKey_MouseLeft, ImGuiKey_GamepadDpadUp...).\n    // - before v1.87, we used ImGuiKey to carry native/user indices as defined by each backends. About use of those legacy ImGuiKey values:\n    //  - without IMGUI_DISABLE_OBSOLETE_KEYIO (legacy support): you can still use your legacy native/user indices (< 512) according to how your backend/engine stored them in io.KeysDown[], but need to cast them to ImGuiKey.\n    //  - with    IMGUI_DISABLE_OBSOLETE_KEYIO (this is the way forward): any use of ImGuiKey will assert with key < 512. GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined).\n    IMGUI_API bool          IsKeyDown(ImGuiKey key);                                            // is key being held.\n    IMGUI_API bool          IsKeyPressed(ImGuiKey key, bool repeat = true);                     // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate\n    IMGUI_API bool          IsKeyReleased(ImGuiKey key);                                        // was key released (went from Down to !Down)?\n    IMGUI_API bool          IsKeyChordPressed(ImGuiKeyChord key_chord);                         // was key chord (mods + key) pressed, e.g. you can pass 'ImGuiMod_Ctrl | ImGuiKey_S' as a key-chord. This doesn't do any routing or focus check, please consider using Shortcut() function instead.\n    IMGUI_API int           GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float rate);  // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate\n    IMGUI_API const char*   GetKeyName(ImGuiKey key);                                           // [DEBUG] returns English name of the key. Those names a provided for debugging purpose and are not meant to be saved persistently not compared.\n    IMGUI_API void          SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard);        // Override io.WantCaptureKeyboard flag next frame (said flag is left for your application to handle, typically when true it instructs your app to ignore inputs). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting \"io.WantCaptureKeyboard = want_capture_keyboard\"; after the next NewFrame() call.\n\n    // Inputs Utilities: Mouse specific\n    // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right.\n    // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle.\n    // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold')\n    IMGUI_API bool          IsMouseDown(ImGuiMouseButton button);                               // is mouse button held?\n    IMGUI_API bool          IsMouseClicked(ImGuiMouseButton button, bool repeat = false);       // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1.\n    IMGUI_API bool          IsMouseReleased(ImGuiMouseButton button);                           // did mouse button released? (went from Down to !Down)\n    IMGUI_API bool          IsMouseDoubleClicked(ImGuiMouseButton button);                      // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true)\n    IMGUI_API int           GetMouseClickedCount(ImGuiMouseButton button);                      // return the number of successive mouse-clicks at the time where a click happen (otherwise 0).\n    IMGUI_API bool          IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block.\n    IMGUI_API bool          IsMousePosValid(const ImVec2* mouse_pos = NULL);                    // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available\n    IMGUI_API bool          IsAnyMouseDown();                                                   // [WILL OBSOLETE] is any mouse button held? This was designed for backends, but prefer having backend maintain a mask of held mouse buttons, because upcoming input queue system will make this invalid.\n    IMGUI_API ImVec2        GetMousePos();                                                      // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls\n    IMGUI_API ImVec2        GetMousePosOnOpeningCurrentPopup();                                 // retrieve mouse position at the time of opening popup we have BeginPopup() into (helper to avoid user backing that value themselves)\n    IMGUI_API bool          IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f);         // is mouse dragging? (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold)\n    IMGUI_API ImVec2        GetMouseDragDelta(ImGuiMouseButton button = 0, float lock_threshold = -1.0f);   // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold)\n    IMGUI_API void          ResetMouseDragDelta(ImGuiMouseButton button = 0);                   //\n    IMGUI_API ImGuiMouseCursor GetMouseCursor();                                                // get desired mouse cursor shape. Important: reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you\n    IMGUI_API void          SetMouseCursor(ImGuiMouseCursor cursor_type);                       // set desired mouse cursor shape\n    IMGUI_API void          SetNextFrameWantCaptureMouse(bool want_capture_mouse);              // Override io.WantCaptureMouse flag next frame (said flag is left for your application to handle, typical when true it instucts your app to ignore inputs). This is equivalent to setting \"io.WantCaptureMouse = want_capture_mouse;\" after the next NewFrame() call.\n\n    // Clipboard Utilities\n    // - Also see the LogToClipboard() function to capture GUI into clipboard, or easily output text data to the clipboard.\n    IMGUI_API const char*   GetClipboardText();\n    IMGUI_API void          SetClipboardText(const char* text);\n\n    // Settings/.Ini Utilities\n    // - The disk functions are automatically called if io.IniFilename != NULL (default is \"imgui.ini\").\n    // - Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually.\n    // - Important: default value \"imgui.ini\" is relative to current working dir! Most apps will want to lock this to an absolute path (e.g. same path as executables).\n    IMGUI_API void          LoadIniSettingsFromDisk(const char* ini_filename);                  // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename).\n    IMGUI_API void          LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source.\n    IMGUI_API void          SaveIniSettingsToDisk(const char* ini_filename);                    // this is automatically called (if io.IniFilename is not empty) a few seconds after any modification that should be reflected in the .ini file (and also by DestroyContext).\n    IMGUI_API const char*   SaveIniSettingsToMemory(size_t* out_ini_size = NULL);               // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings.\n\n    // Debug Utilities\n    IMGUI_API void          DebugTextEncoding(const char* text);\n    IMGUI_API bool          DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert, size_t sz_drawidx); // This is called by IMGUI_CHECKVERSION() macro.\n\n    // Memory Allocators\n    // - Those functions are not reliant on the current context.\n    // - DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()\n    //   for each static/DLL boundary you are calling from. Read \"Context and Memory Allocators\" section of imgui.cpp for more details.\n    IMGUI_API void          SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data = NULL);\n    IMGUI_API void          GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data);\n    IMGUI_API void*         MemAlloc(size_t size);\n    IMGUI_API void          MemFree(void* ptr);\n\n} // namespace ImGui\n\n//-----------------------------------------------------------------------------\n// [SECTION] Flags & Enumerations\n//-----------------------------------------------------------------------------\n\n// Flags for ImGui::Begin()\n// (Those are per-window flags. There are shared flags in ImGuiIO: io.ConfigWindowsResizeFromEdges and io.ConfigWindowsMoveFromTitleBarOnly)\nenum ImGuiWindowFlags_\n{\n    ImGuiWindowFlags_None                   = 0,\n    ImGuiWindowFlags_NoTitleBar             = 1 << 0,   // Disable title-bar\n    ImGuiWindowFlags_NoResize               = 1 << 1,   // Disable user resizing with the lower-right grip\n    ImGuiWindowFlags_NoMove                 = 1 << 2,   // Disable user moving the window\n    ImGuiWindowFlags_NoScrollbar            = 1 << 3,   // Disable scrollbars (window can still scroll with mouse or programmatically)\n    ImGuiWindowFlags_NoScrollWithMouse      = 1 << 4,   // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set.\n    ImGuiWindowFlags_NoCollapse             = 1 << 5,   // Disable user collapsing window by double-clicking on it. Also referred to as Window Menu Button (e.g. within a docking node).\n    ImGuiWindowFlags_AlwaysAutoResize       = 1 << 6,   // Resize every window to its content every frame\n    ImGuiWindowFlags_NoBackground           = 1 << 7,   // Disable drawing background color (WindowBg, etc.) and outside border. Similar as using SetNextWindowBgAlpha(0.0f).\n    ImGuiWindowFlags_NoSavedSettings        = 1 << 8,   // Never load/save settings in .ini file\n    ImGuiWindowFlags_NoMouseInputs          = 1 << 9,   // Disable catching mouse, hovering test with pass through.\n    ImGuiWindowFlags_MenuBar                = 1 << 10,  // Has a menu-bar\n    ImGuiWindowFlags_HorizontalScrollbar    = 1 << 11,  // Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the \"Horizontal Scrolling\" section.\n    ImGuiWindowFlags_NoFocusOnAppearing     = 1 << 12,  // Disable taking focus when transitioning from hidden to visible state\n    ImGuiWindowFlags_NoBringToFrontOnFocus  = 1 << 13,  // Disable bringing window to front when taking focus (e.g. clicking on it or programmatically giving it focus)\n    ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14,  // Always show vertical scrollbar (even if ContentSize.y < Size.y)\n    ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15,  // Always show horizontal scrollbar (even if ContentSize.x < Size.x)\n    ImGuiWindowFlags_NoNavInputs            = 1 << 16,  // No gamepad/keyboard navigation within the window\n    ImGuiWindowFlags_NoNavFocus             = 1 << 17,  // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB)\n    ImGuiWindowFlags_UnsavedDocument        = 1 << 18,  // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.\n    ImGuiWindowFlags_NoNav                  = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,\n    ImGuiWindowFlags_NoDecoration           = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse,\n    ImGuiWindowFlags_NoInputs               = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus,\n\n    // [Internal]\n    ImGuiWindowFlags_NavFlattened           = 1 << 23,  // [BETA] On child window: allow gamepad/keyboard navigation to cross over parent border to this child or between sibling child windows.\n    ImGuiWindowFlags_ChildWindow            = 1 << 24,  // Don't use! For internal use by BeginChild()\n    ImGuiWindowFlags_Tooltip                = 1 << 25,  // Don't use! For internal use by BeginTooltip()\n    ImGuiWindowFlags_Popup                  = 1 << 26,  // Don't use! For internal use by BeginPopup()\n    ImGuiWindowFlags_Modal                  = 1 << 27,  // Don't use! For internal use by BeginPopupModal()\n    ImGuiWindowFlags_ChildMenu              = 1 << 28,  // Don't use! For internal use by BeginMenu()\n\n    // Obsolete names\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 30,  // Obsoleted in 1.90: Use ImGuiChildFlags_AlwaysUseWindowPadding in BeginChild() call.\n#endif\n};\n\n// Flags for ImGui::BeginChild()\n// (Legacy: bot 0 must always correspond to ImGuiChildFlags_Border to be backward compatible with old API using 'bool border = false'.\n// About using AutoResizeX/AutoResizeY flags:\n// - May be combined with SetNextWindowSizeConstraints() to set a min/max size for each axis (see \"Demo->Child->Auto-resize with Constraints\").\n// - Size measurement for a given axis is only performed when the child window is within visible boundaries, or is just appearing.\n//   - This allows BeginChild() to return false when not within boundaries (e.g. when scrolling), which is more optimal. BUT it won't update its auto-size while clipped.\n//     While not perfect, it is a better default behavior as the always-on performance gain is more valuable than the occasional \"resizing after becoming visible again\" glitch.\n//   - You may also use ImGuiChildFlags_AlwaysAutoResize to force an update even when child window is not in view.\n//     HOWEVER PLEASE UNDERSTAND THAT DOING SO WILL PREVENT BeginChild() FROM EVER RETURNING FALSE, disabling benefits of coarse clipping.\nenum ImGuiChildFlags_\n{\n    ImGuiChildFlags_None                    = 0,\n    ImGuiChildFlags_Border                  = 1 << 0,   // Show an outer border and enable WindowPadding. (Important: this is always == 1 == true for legacy reason)\n    ImGuiChildFlags_AlwaysUseWindowPadding  = 1 << 1,   // Pad with style.WindowPadding even if no border are drawn (no padding by default for non-bordered child windows because it makes more sense)\n    ImGuiChildFlags_ResizeX                 = 1 << 2,   // Allow resize from right border (layout direction). Enable .ini saving (unless ImGuiWindowFlags_NoSavedSettings passed to window flags)\n    ImGuiChildFlags_ResizeY                 = 1 << 3,   // Allow resize from bottom border (layout direction). \"\n    ImGuiChildFlags_AutoResizeX             = 1 << 4,   // Enable auto-resizing width. Read \"IMPORTANT: Size measurement\" details above.\n    ImGuiChildFlags_AutoResizeY             = 1 << 5,   // Enable auto-resizing height. Read \"IMPORTANT: Size measurement\" details above.\n    ImGuiChildFlags_AlwaysAutoResize        = 1 << 6,   // Combined with AutoResizeX/AutoResizeY. Always measure size even when child is hidden, always return true, always disable clipping optimization! NOT RECOMMENDED.\n    ImGuiChildFlags_FrameStyle              = 1 << 7,   // Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.\n};\n\n// Flags for ImGui::InputText()\n// (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigInputTextCursorBlink and io.ConfigInputTextEnterKeepActive)\nenum ImGuiInputTextFlags_\n{\n    ImGuiInputTextFlags_None                = 0,\n    ImGuiInputTextFlags_CharsDecimal        = 1 << 0,   // Allow 0123456789.+-*/\n    ImGuiInputTextFlags_CharsHexadecimal    = 1 << 1,   // Allow 0123456789ABCDEFabcdef\n    ImGuiInputTextFlags_CharsUppercase      = 1 << 2,   // Turn a..z into A..Z\n    ImGuiInputTextFlags_CharsNoBlank        = 1 << 3,   // Filter out spaces, tabs\n    ImGuiInputTextFlags_AutoSelectAll       = 1 << 4,   // Select entire text when first taking mouse focus\n    ImGuiInputTextFlags_EnterReturnsTrue    = 1 << 5,   // Return 'true' when Enter is pressed (as opposed to every time the value was modified). Consider looking at the IsItemDeactivatedAfterEdit() function.\n    ImGuiInputTextFlags_CallbackCompletion  = 1 << 6,   // Callback on pressing TAB (for completion handling)\n    ImGuiInputTextFlags_CallbackHistory     = 1 << 7,   // Callback on pressing Up/Down arrows (for history handling)\n    ImGuiInputTextFlags_CallbackAlways      = 1 << 8,   // Callback on each iteration. User code may query cursor position, modify text buffer.\n    ImGuiInputTextFlags_CallbackCharFilter  = 1 << 9,   // Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard.\n    ImGuiInputTextFlags_AllowTabInput       = 1 << 10,  // Pressing TAB input a '\\t' character into the text field\n    ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11,  // In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is opposite: unfocus with Ctrl+Enter, add line with Enter).\n    ImGuiInputTextFlags_NoHorizontalScroll  = 1 << 12,  // Disable following the cursor horizontally\n    ImGuiInputTextFlags_AlwaysOverwrite     = 1 << 13,  // Overwrite mode\n    ImGuiInputTextFlags_ReadOnly            = 1 << 14,  // Read-only mode\n    ImGuiInputTextFlags_Password            = 1 << 15,  // Password mode, display all characters as '*'\n    ImGuiInputTextFlags_NoUndoRedo          = 1 << 16,  // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID().\n    ImGuiInputTextFlags_CharsScientific     = 1 << 17,  // Allow 0123456789.+-*/eE (Scientific notation input)\n    ImGuiInputTextFlags_CallbackResize      = 1 << 18,  // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this)\n    ImGuiInputTextFlags_CallbackEdit        = 1 << 19,  // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active)\n    ImGuiInputTextFlags_EscapeClearsAll     = 1 << 20,  // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert)\n\n    // Obsolete names\n    //ImGuiInputTextFlags_AlwaysInsertMode  = ImGuiInputTextFlags_AlwaysOverwrite   // [renamed in 1.82] name was not matching behavior\n};\n\n// Flags for ImGui::TreeNodeEx(), ImGui::CollapsingHeader*()\nenum ImGuiTreeNodeFlags_\n{\n    ImGuiTreeNodeFlags_None                 = 0,\n    ImGuiTreeNodeFlags_Selected             = 1 << 0,   // Draw as selected\n    ImGuiTreeNodeFlags_Framed               = 1 << 1,   // Draw frame with background (e.g. for CollapsingHeader)\n    ImGuiTreeNodeFlags_AllowOverlap         = 1 << 2,   // Hit testing to allow subsequent widgets to overlap this one\n    ImGuiTreeNodeFlags_NoTreePushOnOpen     = 1 << 3,   // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack\n    ImGuiTreeNodeFlags_NoAutoOpenOnLog      = 1 << 4,   // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes)\n    ImGuiTreeNodeFlags_DefaultOpen          = 1 << 5,   // Default node to be open\n    ImGuiTreeNodeFlags_OpenOnDoubleClick    = 1 << 6,   // Need double-click to open node\n    ImGuiTreeNodeFlags_OpenOnArrow          = 1 << 7,   // Only open when clicking on the arrow part. If ImGuiTreeNodeFlags_OpenOnDoubleClick is also set, single-click arrow or double-click all box to open.\n    ImGuiTreeNodeFlags_Leaf                 = 1 << 8,   // No collapsing, no arrow (use as a convenience for leaf nodes).\n    ImGuiTreeNodeFlags_Bullet               = 1 << 9,   // Display a bullet instead of arrow. IMPORTANT: node can still be marked open/close if you don't set the _Leaf flag!\n    ImGuiTreeNodeFlags_FramePadding         = 1 << 10,  // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding().\n    ImGuiTreeNodeFlags_SpanAvailWidth       = 1 << 11,  // Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line. In the future we may refactor the hit system to be front-to-back, allowing natural overlaps and then this can become the default.\n    ImGuiTreeNodeFlags_SpanFullWidth        = 1 << 12,  // Extend hit box to the left-most and right-most edges (bypass the indented area).\n    ImGuiTreeNodeFlags_SpanAllColumns       = 1 << 13,  // Frame will span all columns of its container table (text will still fit in current column)\n    ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 14,  // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop)\n    //ImGuiTreeNodeFlags_NoScrollOnOpen     = 1 << 14,  // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible\n    ImGuiTreeNodeFlags_CollapsingHeader     = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog,\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiTreeNodeFlags_AllowItemOverlap     = ImGuiTreeNodeFlags_AllowOverlap,  // Renamed in 1.89.7\n#endif\n};\n\n// Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions.\n// - To be backward compatible with older API which took an 'int mouse_button = 1' argument, we need to treat\n//   small flags values as a mouse button index, so we encode the mouse button in the first few bits of the flags.\n//   It is therefore guaranteed to be legal to pass a mouse button index in ImGuiPopupFlags.\n// - For the same reason, we exceptionally default the ImGuiPopupFlags argument of BeginPopupContextXXX functions to 1 instead of 0.\n//   IMPORTANT: because the default parameter is 1 (==ImGuiPopupFlags_MouseButtonRight), if you rely on the default parameter\n//   and want to use another flag, you need to pass in the ImGuiPopupFlags_MouseButtonRight flag explicitly.\n// - Multiple buttons currently cannot be combined/or-ed in those functions (we could allow it later).\nenum ImGuiPopupFlags_\n{\n    ImGuiPopupFlags_None                    = 0,\n    ImGuiPopupFlags_MouseButtonLeft         = 0,        // For BeginPopupContext*(): open on Left Mouse release. Guaranteed to always be == 0 (same as ImGuiMouseButton_Left)\n    ImGuiPopupFlags_MouseButtonRight        = 1,        // For BeginPopupContext*(): open on Right Mouse release. Guaranteed to always be == 1 (same as ImGuiMouseButton_Right)\n    ImGuiPopupFlags_MouseButtonMiddle       = 2,        // For BeginPopupContext*(): open on Middle Mouse release. Guaranteed to always be == 2 (same as ImGuiMouseButton_Middle)\n    ImGuiPopupFlags_MouseButtonMask_        = 0x1F,\n    ImGuiPopupFlags_MouseButtonDefault_     = 1,\n    ImGuiPopupFlags_NoOpenOverExistingPopup = 1 << 5,   // For OpenPopup*(), BeginPopupContext*(): don't open if there's already a popup at the same level of the popup stack\n    ImGuiPopupFlags_NoOpenOverItems         = 1 << 6,   // For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space\n    ImGuiPopupFlags_AnyPopupId              = 1 << 7,   // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup.\n    ImGuiPopupFlags_AnyPopupLevel           = 1 << 8,   // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level)\n    ImGuiPopupFlags_AnyPopup                = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel,\n};\n\n// Flags for ImGui::Selectable()\nenum ImGuiSelectableFlags_\n{\n    ImGuiSelectableFlags_None               = 0,\n    ImGuiSelectableFlags_DontClosePopups    = 1 << 0,   // Clicking this doesn't close parent popup window\n    ImGuiSelectableFlags_SpanAllColumns     = 1 << 1,   // Frame will span all columns of its container table (text will still fit in current column)\n    ImGuiSelectableFlags_AllowDoubleClick   = 1 << 2,   // Generate press events on double clicks too\n    ImGuiSelectableFlags_Disabled           = 1 << 3,   // Cannot be selected, display grayed out text\n    ImGuiSelectableFlags_AllowOverlap       = 1 << 4,   // (WIP) Hit testing to allow subsequent widgets to overlap this one\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiSelectableFlags_AllowItemOverlap   = ImGuiSelectableFlags_AllowOverlap,  // Renamed in 1.89.7\n#endif\n};\n\n// Flags for ImGui::BeginCombo()\nenum ImGuiComboFlags_\n{\n    ImGuiComboFlags_None                    = 0,\n    ImGuiComboFlags_PopupAlignLeft          = 1 << 0,   // Align the popup toward the left by default\n    ImGuiComboFlags_HeightSmall             = 1 << 1,   // Max ~4 items visible. Tip: If you want your combo popup to be a specific size you can use SetNextWindowSizeConstraints() prior to calling BeginCombo()\n    ImGuiComboFlags_HeightRegular           = 1 << 2,   // Max ~8 items visible (default)\n    ImGuiComboFlags_HeightLarge             = 1 << 3,   // Max ~20 items visible\n    ImGuiComboFlags_HeightLargest           = 1 << 4,   // As many fitting items as possible\n    ImGuiComboFlags_NoArrowButton           = 1 << 5,   // Display on the preview box without the square arrow button\n    ImGuiComboFlags_NoPreview               = 1 << 6,   // Display only a square arrow button\n    ImGuiComboFlags_WidthFitPreview         = 1 << 7,   // Width dynamically calculated from preview contents\n    ImGuiComboFlags_HeightMask_             = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest,\n};\n\n// Flags for ImGui::BeginTabBar()\nenum ImGuiTabBarFlags_\n{\n    ImGuiTabBarFlags_None                           = 0,\n    ImGuiTabBarFlags_Reorderable                    = 1 << 0,   // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list\n    ImGuiTabBarFlags_AutoSelectNewTabs              = 1 << 1,   // Automatically select new tabs when they appear\n    ImGuiTabBarFlags_TabListPopupButton             = 1 << 2,   // Disable buttons to open the tab list popup\n    ImGuiTabBarFlags_NoCloseWithMiddleMouseButton   = 1 << 3,   // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.\n    ImGuiTabBarFlags_NoTabListScrollingButtons      = 1 << 4,   // Disable scrolling buttons (apply when fitting policy is ImGuiTabBarFlags_FittingPolicyScroll)\n    ImGuiTabBarFlags_NoTooltip                      = 1 << 5,   // Disable tooltips when hovering a tab\n    ImGuiTabBarFlags_FittingPolicyResizeDown        = 1 << 6,   // Resize tabs when they don't fit\n    ImGuiTabBarFlags_FittingPolicyScroll            = 1 << 7,   // Add scroll buttons when tabs don't fit\n    ImGuiTabBarFlags_FittingPolicyMask_             = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll,\n    ImGuiTabBarFlags_FittingPolicyDefault_          = ImGuiTabBarFlags_FittingPolicyResizeDown,\n};\n\n// Flags for ImGui::BeginTabItem()\nenum ImGuiTabItemFlags_\n{\n    ImGuiTabItemFlags_None                          = 0,\n    ImGuiTabItemFlags_UnsavedDocument               = 1 << 0,   // Display a dot next to the title + tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.\n    ImGuiTabItemFlags_SetSelected                   = 1 << 1,   // Trigger flag to programmatically make the tab selected when calling BeginTabItem()\n    ImGuiTabItemFlags_NoCloseWithMiddleMouseButton  = 1 << 2,   // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.\n    ImGuiTabItemFlags_NoPushId                      = 1 << 3,   // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem()\n    ImGuiTabItemFlags_NoTooltip                     = 1 << 4,   // Disable tooltip for the given tab\n    ImGuiTabItemFlags_NoReorder                     = 1 << 5,   // Disable reordering this tab or having another tab cross over this tab\n    ImGuiTabItemFlags_Leading                       = 1 << 6,   // Enforce the tab position to the left of the tab bar (after the tab list popup button)\n    ImGuiTabItemFlags_Trailing                      = 1 << 7,   // Enforce the tab position to the right of the tab bar (before the scrolling buttons)\n};\n\n// Flags for ImGui::BeginTable()\n// - Important! Sizing policies have complex and subtle side effects, much more so than you would expect.\n//   Read comments/demos carefully + experiment with live demos to get acquainted with them.\n// - The DEFAULT sizing policies are:\n//    - Default to ImGuiTableFlags_SizingFixedFit    if ScrollX is on, or if host window has ImGuiWindowFlags_AlwaysAutoResize.\n//    - Default to ImGuiTableFlags_SizingStretchSame if ScrollX is off.\n// - When ScrollX is off:\n//    - Table defaults to ImGuiTableFlags_SizingStretchSame -> all Columns defaults to ImGuiTableColumnFlags_WidthStretch with same weight.\n//    - Columns sizing policy allowed: Stretch (default), Fixed/Auto.\n//    - Fixed Columns (if any) will generally obtain their requested width (unless the table cannot fit them all).\n//    - Stretch Columns will share the remaining width according to their respective weight.\n//    - Mixed Fixed/Stretch columns is possible but has various side-effects on resizing behaviors.\n//      The typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.\n//      (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing).\n// - When ScrollX is on:\n//    - Table defaults to ImGuiTableFlags_SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed\n//    - Columns sizing policy allowed: Fixed/Auto mostly.\n//    - Fixed Columns can be enlarged as needed. Table will show a horizontal scrollbar if needed.\n//    - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop.\n//    - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable().\n//      If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again.\n// - Read on documentation at the top of imgui_tables.cpp for details.\nenum ImGuiTableFlags_\n{\n    // Features\n    ImGuiTableFlags_None                       = 0,\n    ImGuiTableFlags_Resizable                  = 1 << 0,   // Enable resizing columns.\n    ImGuiTableFlags_Reorderable                = 1 << 1,   // Enable reordering columns in header row (need calling TableSetupColumn() + TableHeadersRow() to display headers)\n    ImGuiTableFlags_Hideable                   = 1 << 2,   // Enable hiding/disabling columns in context menu.\n    ImGuiTableFlags_Sortable                   = 1 << 3,   // Enable sorting. Call TableGetSortSpecs() to obtain sort specs. Also see ImGuiTableFlags_SortMulti and ImGuiTableFlags_SortTristate.\n    ImGuiTableFlags_NoSavedSettings            = 1 << 4,   // Disable persisting columns order, width and sort settings in the .ini file.\n    ImGuiTableFlags_ContextMenuInBody          = 1 << 5,   // Right-click on columns body/contents will display table context menu. By default it is available in TableHeadersRow().\n    // Decorations\n    ImGuiTableFlags_RowBg                      = 1 << 6,   // Set each RowBg color with ImGuiCol_TableRowBg or ImGuiCol_TableRowBgAlt (equivalent of calling TableSetBgColor with ImGuiTableBgFlags_RowBg0 on each row manually)\n    ImGuiTableFlags_BordersInnerH              = 1 << 7,   // Draw horizontal borders between rows.\n    ImGuiTableFlags_BordersOuterH              = 1 << 8,   // Draw horizontal borders at the top and bottom.\n    ImGuiTableFlags_BordersInnerV              = 1 << 9,   // Draw vertical borders between columns.\n    ImGuiTableFlags_BordersOuterV              = 1 << 10,  // Draw vertical borders on the left and right sides.\n    ImGuiTableFlags_BordersH                   = ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersOuterH, // Draw horizontal borders.\n    ImGuiTableFlags_BordersV                   = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuterV, // Draw vertical borders.\n    ImGuiTableFlags_BordersInner               = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders.\n    ImGuiTableFlags_BordersOuter               = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders.\n    ImGuiTableFlags_Borders                    = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter,   // Draw all borders.\n    ImGuiTableFlags_NoBordersInBody            = 1 << 11,  // [ALPHA] Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style\n    ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12,  // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style\n    // Sizing Policy (read above for defaults)\n    ImGuiTableFlags_SizingFixedFit             = 1 << 13,  // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width.\n    ImGuiTableFlags_SizingFixedSame            = 2 << 13,  // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible.\n    ImGuiTableFlags_SizingStretchProp          = 3 << 13,  // Columns default to _WidthStretch with default weights proportional to each columns contents widths.\n    ImGuiTableFlags_SizingStretchSame          = 4 << 13,  // Columns default to _WidthStretch with default weights all equal, unless overridden by TableSetupColumn().\n    // Sizing Extra Options\n    ImGuiTableFlags_NoHostExtendX              = 1 << 16,  // Make outer width auto-fit to columns, overriding outer_size.x value. Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.\n    ImGuiTableFlags_NoHostExtendY              = 1 << 17,  // Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.\n    ImGuiTableFlags_NoKeepColumnsVisible       = 1 << 18,  // Disable keeping column always minimally visible when ScrollX is off and table gets too small. Not recommended if columns are resizable.\n    ImGuiTableFlags_PreciseWidths              = 1 << 19,  // Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.\n    // Clipping\n    ImGuiTableFlags_NoClip                     = 1 << 20,  // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze().\n    // Padding\n    ImGuiTableFlags_PadOuterX                  = 1 << 21,  // Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers.\n    ImGuiTableFlags_NoPadOuterX                = 1 << 22,  // Default if BordersOuterV is off. Disable outermost padding.\n    ImGuiTableFlags_NoPadInnerX                = 1 << 23,  // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off).\n    // Scrolling\n    ImGuiTableFlags_ScrollX                    = 1 << 24,  // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX.\n    ImGuiTableFlags_ScrollY                    = 1 << 25,  // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size.\n    // Sorting\n    ImGuiTableFlags_SortMulti                  = 1 << 26,  // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).\n    ImGuiTableFlags_SortTristate               = 1 << 27,  // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).\n    // Miscellaneous\n    ImGuiTableFlags_HighlightHoveredColumn     = 1 << 28,  // Highlight column headers when hovered (may evolve into a fuller highlight)\n\n    // [Internal] Combinations and masks\n    ImGuiTableFlags_SizingMask_                = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame,\n};\n\n// Flags for ImGui::TableSetupColumn()\nenum ImGuiTableColumnFlags_\n{\n    // Input configuration flags\n    ImGuiTableColumnFlags_None                  = 0,\n    ImGuiTableColumnFlags_Disabled              = 1 << 0,   // Overriding/master disable flag: hide column, won't show in context menu (unlike calling TableSetColumnEnabled() which manipulates the user accessible state)\n    ImGuiTableColumnFlags_DefaultHide           = 1 << 1,   // Default as a hidden/disabled column.\n    ImGuiTableColumnFlags_DefaultSort           = 1 << 2,   // Default as a sorting column.\n    ImGuiTableColumnFlags_WidthStretch          = 1 << 3,   // Column will stretch. Preferable with horizontal scrolling disabled (default if table sizing policy is _SizingStretchSame or _SizingStretchProp).\n    ImGuiTableColumnFlags_WidthFixed            = 1 << 4,   // Column will not stretch. Preferable with horizontal scrolling enabled (default if table sizing policy is _SizingFixedFit and table is resizable).\n    ImGuiTableColumnFlags_NoResize              = 1 << 5,   // Disable manual resizing.\n    ImGuiTableColumnFlags_NoReorder             = 1 << 6,   // Disable manual reordering this column, this will also prevent other columns from crossing over this column.\n    ImGuiTableColumnFlags_NoHide                = 1 << 7,   // Disable ability to hide/disable this column.\n    ImGuiTableColumnFlags_NoClip                = 1 << 8,   // Disable clipping for this column (all NoClip columns will render in a same draw command).\n    ImGuiTableColumnFlags_NoSort                = 1 << 9,   // Disable ability to sort on this field (even if ImGuiTableFlags_Sortable is set on the table).\n    ImGuiTableColumnFlags_NoSortAscending       = 1 << 10,  // Disable ability to sort in the ascending direction.\n    ImGuiTableColumnFlags_NoSortDescending      = 1 << 11,  // Disable ability to sort in the descending direction.\n    ImGuiTableColumnFlags_NoHeaderLabel         = 1 << 12,  // TableHeadersRow() will not submit horizontal label for this column. Convenient for some small columns. Name will still appear in context menu or in angled headers.\n    ImGuiTableColumnFlags_NoHeaderWidth         = 1 << 13,  // Disable header text width contribution to automatic column width.\n    ImGuiTableColumnFlags_PreferSortAscending   = 1 << 14,  // Make the initial sort direction Ascending when first sorting on this column (default).\n    ImGuiTableColumnFlags_PreferSortDescending  = 1 << 15,  // Make the initial sort direction Descending when first sorting on this column.\n    ImGuiTableColumnFlags_IndentEnable          = 1 << 16,  // Use current Indent value when entering cell (default for column 0).\n    ImGuiTableColumnFlags_IndentDisable         = 1 << 17,  // Ignore current Indent value when entering cell (default for columns > 0). Indentation changes _within_ the cell will still be honored.\n    ImGuiTableColumnFlags_AngledHeader          = 1 << 18,  // TableHeadersRow() will submit an angled header row for this column. Note this will add an extra row.\n\n    // Output status flags, read-only via TableGetColumnFlags()\n    ImGuiTableColumnFlags_IsEnabled             = 1 << 24,  // Status: is enabled == not hidden by user/api (referred to as \"Hide\" in _DefaultHide and _NoHide) flags.\n    ImGuiTableColumnFlags_IsVisible             = 1 << 25,  // Status: is visible == is enabled AND not clipped by scrolling.\n    ImGuiTableColumnFlags_IsSorted              = 1 << 26,  // Status: is currently part of the sort specs\n    ImGuiTableColumnFlags_IsHovered             = 1 << 27,  // Status: is hovered by mouse\n\n    // [Internal] Combinations and masks\n    ImGuiTableColumnFlags_WidthMask_            = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed,\n    ImGuiTableColumnFlags_IndentMask_           = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable,\n    ImGuiTableColumnFlags_StatusMask_           = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered,\n    ImGuiTableColumnFlags_NoDirectResize_       = 1 << 30,  // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge)\n};\n\n// Flags for ImGui::TableNextRow()\nenum ImGuiTableRowFlags_\n{\n    ImGuiTableRowFlags_None                     = 0,\n    ImGuiTableRowFlags_Headers                  = 1 << 0,   // Identify header row (set default background color + width of its contents accounted differently for auto column width)\n};\n\n// Enum for ImGui::TableSetBgColor()\n// Background colors are rendering in 3 layers:\n//  - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.\n//  - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.\n//  - Layer 2: draw with CellBg color if set.\n// The purpose of the two row/columns layers is to let you decide if a background color change should override or blend with the existing color.\n// When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows.\n// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.\n// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.\nenum ImGuiTableBgTarget_\n{\n    ImGuiTableBgTarget_None                     = 0,\n    ImGuiTableBgTarget_RowBg0                   = 1,        // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used)\n    ImGuiTableBgTarget_RowBg1                   = 2,        // Set row background color 1 (generally used for selection marking)\n    ImGuiTableBgTarget_CellBg                   = 3,        // Set cell background color (top-most color)\n};\n\n// Flags for ImGui::IsWindowFocused()\nenum ImGuiFocusedFlags_\n{\n    ImGuiFocusedFlags_None                          = 0,\n    ImGuiFocusedFlags_ChildWindows                  = 1 << 0,   // Return true if any children of the window is focused\n    ImGuiFocusedFlags_RootWindow                    = 1 << 1,   // Test from root window (top most parent of the current hierarchy)\n    ImGuiFocusedFlags_AnyWindow                     = 1 << 2,   // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ!\n    ImGuiFocusedFlags_NoPopupHierarchy              = 1 << 3,   // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)\n    //ImGuiFocusedFlags_DockHierarchy               = 1 << 4,   // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)\n    ImGuiFocusedFlags_RootAndChildWindows           = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows,\n};\n\n// Flags for ImGui::IsItemHovered(), ImGui::IsWindowHovered()\n// Note: if you are trying to check whether your mouse should be dispatched to Dear ImGui or to your app, you should use 'io.WantCaptureMouse' instead! Please read the FAQ!\n// Note: windows with the ImGuiWindowFlags_NoInputs flag are ignored by IsWindowHovered() calls.\nenum ImGuiHoveredFlags_\n{\n    ImGuiHoveredFlags_None                          = 0,        // Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them.\n    ImGuiHoveredFlags_ChildWindows                  = 1 << 0,   // IsWindowHovered() only: Return true if any children of the window is hovered\n    ImGuiHoveredFlags_RootWindow                    = 1 << 1,   // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy)\n    ImGuiHoveredFlags_AnyWindow                     = 1 << 2,   // IsWindowHovered() only: Return true if any window is hovered\n    ImGuiHoveredFlags_NoPopupHierarchy              = 1 << 3,   // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)\n    //ImGuiHoveredFlags_DockHierarchy               = 1 << 4,   // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)\n    ImGuiHoveredFlags_AllowWhenBlockedByPopup       = 1 << 5,   // Return true even if a popup window is normally blocking access to this item/window\n    //ImGuiHoveredFlags_AllowWhenBlockedByModal     = 1 << 6,   // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet.\n    ImGuiHoveredFlags_AllowWhenBlockedByActiveItem  = 1 << 7,   // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns.\n    ImGuiHoveredFlags_AllowWhenOverlappedByItem     = 1 << 8,   // IsItemHovered() only: Return true even if the item uses AllowOverlap mode and is overlapped by another hoverable item.\n    ImGuiHoveredFlags_AllowWhenOverlappedByWindow   = 1 << 9,   // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window.\n    ImGuiHoveredFlags_AllowWhenDisabled             = 1 << 10,  // IsItemHovered() only: Return true even if the item is disabled\n    ImGuiHoveredFlags_NoNavOverride                 = 1 << 11,  // IsItemHovered() only: Disable using gamepad/keyboard navigation state when active, always query mouse\n    ImGuiHoveredFlags_AllowWhenOverlapped           = ImGuiHoveredFlags_AllowWhenOverlappedByItem | ImGuiHoveredFlags_AllowWhenOverlappedByWindow,\n    ImGuiHoveredFlags_RectOnly                      = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped,\n    ImGuiHoveredFlags_RootAndChildWindows           = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows,\n\n    // Tooltips mode\n    // - typically used in IsItemHovered() + SetTooltip() sequence.\n    // - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior.\n    //   e.g. 'TooltipHoveredFlagsForMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'.\n    // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often.\n    // - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay.\n    ImGuiHoveredFlags_ForTooltip                    = 1 << 12,  // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence.\n\n    // (Advanced) Mouse Hovering delays.\n    // - generally you can use ImGuiHoveredFlags_ForTooltip to use application-standardized flags.\n    // - use those if you need specific overrides.\n    ImGuiHoveredFlags_Stationary                    = 1 << 13,  // Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay.\n    ImGuiHoveredFlags_DelayNone                     = 1 << 14,  // IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this.\n    ImGuiHoveredFlags_DelayShort                    = 1 << 15,  // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).\n    ImGuiHoveredFlags_DelayNormal                   = 1 << 16,  // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).\n    ImGuiHoveredFlags_NoSharedDelay                 = 1 << 17,  // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays)\n};\n\n// Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload()\nenum ImGuiDragDropFlags_\n{\n    ImGuiDragDropFlags_None                         = 0,\n    // BeginDragDropSource() flags\n    ImGuiDragDropFlags_SourceNoPreviewTooltip       = 1 << 0,   // Disable preview tooltip. By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disables this behavior.\n    ImGuiDragDropFlags_SourceNoDisableHover         = 1 << 1,   // By default, when dragging we clear data so that IsItemHovered() will return false, to avoid subsequent user code submitting tooltips. This flag disables this behavior so you can still call IsItemHovered() on the source item.\n    ImGuiDragDropFlags_SourceNoHoldToOpenOthers     = 1 << 2,   // Disable the behavior that allows to open tree nodes and collapsing header by holding over them while dragging a source item.\n    ImGuiDragDropFlags_SourceAllowNullID            = 1 << 3,   // Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit.\n    ImGuiDragDropFlags_SourceExtern                 = 1 << 4,   // External source (from outside of dear imgui), won't attempt to read current item/window info. Will always return true. Only one Extern source can be active simultaneously.\n    ImGuiDragDropFlags_SourceAutoExpirePayload      = 1 << 5,   // Automatically expire the payload if the source cease to be submitted (otherwise payloads are persisting while being dragged)\n    // AcceptDragDropPayload() flags\n    ImGuiDragDropFlags_AcceptBeforeDelivery         = 1 << 10,  // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered.\n    ImGuiDragDropFlags_AcceptNoDrawDefaultRect      = 1 << 11,  // Do not draw the default highlight rectangle when hovering over target.\n    ImGuiDragDropFlags_AcceptNoPreviewTooltip       = 1 << 12,  // Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site.\n    ImGuiDragDropFlags_AcceptPeekOnly               = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect, // For peeking ahead and inspecting the payload before delivery.\n};\n\n// Standard Drag and Drop payload types. You can define you own payload types using short strings. Types starting with '_' are defined by Dear ImGui.\n#define IMGUI_PAYLOAD_TYPE_COLOR_3F     \"_COL3F\"    // float[3]: Standard type for colors, without alpha. User code may use this type.\n#define IMGUI_PAYLOAD_TYPE_COLOR_4F     \"_COL4F\"    // float[4]: Standard type for colors. User code may use this type.\n\n// A primary data type\nenum ImGuiDataType_\n{\n    ImGuiDataType_S8,       // signed char / char (with sensible compilers)\n    ImGuiDataType_U8,       // unsigned char\n    ImGuiDataType_S16,      // short\n    ImGuiDataType_U16,      // unsigned short\n    ImGuiDataType_S32,      // int\n    ImGuiDataType_U32,      // unsigned int\n    ImGuiDataType_S64,      // long long / __int64\n    ImGuiDataType_U64,      // unsigned long long / unsigned __int64\n    ImGuiDataType_Float,    // float\n    ImGuiDataType_Double,   // double\n    ImGuiDataType_COUNT\n};\n\n// A cardinal direction\nenum ImGuiDir_\n{\n    ImGuiDir_None    = -1,\n    ImGuiDir_Left    = 0,\n    ImGuiDir_Right   = 1,\n    ImGuiDir_Up      = 2,\n    ImGuiDir_Down    = 3,\n    ImGuiDir_COUNT\n};\n\n// A sorting direction\nenum ImGuiSortDirection_\n{\n    ImGuiSortDirection_None         = 0,\n    ImGuiSortDirection_Ascending    = 1,    // Ascending = 0->9, A->Z etc.\n    ImGuiSortDirection_Descending   = 2     // Descending = 9->0, Z->A etc.\n};\n\n// Since 1.90, defining IMGUI_DISABLE_OBSOLETE_FUNCTIONS automatically defines IMGUI_DISABLE_OBSOLETE_KEYIO as well.\n#if defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && !defined(IMGUI_DISABLE_OBSOLETE_KEYIO)\n#define IMGUI_DISABLE_OBSOLETE_KEYIO\n#endif\n\n// A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value): can represent Keyboard, Mouse and Gamepad values.\n// All our named keys are >= 512. Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87).\n// Since >= 1.89 we increased typing (went from int to enum), some legacy code may need a cast to ImGuiKey.\n// Read details about the 1.87 and 1.89 transition : https://github.com/ocornut/imgui/issues/4921\n// Note that \"Keys\" related to physical keys and are not the same concept as input \"Characters\", the later are submitted via io.AddInputCharacter().\nenum ImGuiKey : int\n{\n    // Keyboard\n    ImGuiKey_None = 0,\n    ImGuiKey_Tab = 512,             // == ImGuiKey_NamedKey_BEGIN\n    ImGuiKey_LeftArrow,\n    ImGuiKey_RightArrow,\n    ImGuiKey_UpArrow,\n    ImGuiKey_DownArrow,\n    ImGuiKey_PageUp,\n    ImGuiKey_PageDown,\n    ImGuiKey_Home,\n    ImGuiKey_End,\n    ImGuiKey_Insert,\n    ImGuiKey_Delete,\n    ImGuiKey_Backspace,\n    ImGuiKey_Space,\n    ImGuiKey_Enter,\n    ImGuiKey_Escape,\n    ImGuiKey_LeftCtrl, ImGuiKey_LeftShift, ImGuiKey_LeftAlt, ImGuiKey_LeftSuper,\n    ImGuiKey_RightCtrl, ImGuiKey_RightShift, ImGuiKey_RightAlt, ImGuiKey_RightSuper,\n    ImGuiKey_Menu,\n    ImGuiKey_0, ImGuiKey_1, ImGuiKey_2, ImGuiKey_3, ImGuiKey_4, ImGuiKey_5, ImGuiKey_6, ImGuiKey_7, ImGuiKey_8, ImGuiKey_9,\n    ImGuiKey_A, ImGuiKey_B, ImGuiKey_C, ImGuiKey_D, ImGuiKey_E, ImGuiKey_F, ImGuiKey_G, ImGuiKey_H, ImGuiKey_I, ImGuiKey_J,\n    ImGuiKey_K, ImGuiKey_L, ImGuiKey_M, ImGuiKey_N, ImGuiKey_O, ImGuiKey_P, ImGuiKey_Q, ImGuiKey_R, ImGuiKey_S, ImGuiKey_T,\n    ImGuiKey_U, ImGuiKey_V, ImGuiKey_W, ImGuiKey_X, ImGuiKey_Y, ImGuiKey_Z,\n    ImGuiKey_F1, ImGuiKey_F2, ImGuiKey_F3, ImGuiKey_F4, ImGuiKey_F5, ImGuiKey_F6,\n    ImGuiKey_F7, ImGuiKey_F8, ImGuiKey_F9, ImGuiKey_F10, ImGuiKey_F11, ImGuiKey_F12,\n    ImGuiKey_F13, ImGuiKey_F14, ImGuiKey_F15, ImGuiKey_F16, ImGuiKey_F17, ImGuiKey_F18,\n    ImGuiKey_F19, ImGuiKey_F20, ImGuiKey_F21, ImGuiKey_F22, ImGuiKey_F23, ImGuiKey_F24,\n    ImGuiKey_Apostrophe,        // '\n    ImGuiKey_Comma,             // ,\n    ImGuiKey_Minus,             // -\n    ImGuiKey_Period,            // .\n    ImGuiKey_Slash,             // /\n    ImGuiKey_Semicolon,         // ;\n    ImGuiKey_Equal,             // =\n    ImGuiKey_LeftBracket,       // [\n    ImGuiKey_Backslash,         // \\ (this text inhibit multiline comment caused by backslash)\n    ImGuiKey_RightBracket,      // ]\n    ImGuiKey_GraveAccent,       // `\n    ImGuiKey_CapsLock,\n    ImGuiKey_ScrollLock,\n    ImGuiKey_NumLock,\n    ImGuiKey_PrintScreen,\n    ImGuiKey_Pause,\n    ImGuiKey_Keypad0, ImGuiKey_Keypad1, ImGuiKey_Keypad2, ImGuiKey_Keypad3, ImGuiKey_Keypad4,\n    ImGuiKey_Keypad5, ImGuiKey_Keypad6, ImGuiKey_Keypad7, ImGuiKey_Keypad8, ImGuiKey_Keypad9,\n    ImGuiKey_KeypadDecimal,\n    ImGuiKey_KeypadDivide,\n    ImGuiKey_KeypadMultiply,\n    ImGuiKey_KeypadSubtract,\n    ImGuiKey_KeypadAdd,\n    ImGuiKey_KeypadEnter,\n    ImGuiKey_KeypadEqual,\n    ImGuiKey_AppBack,               // Available on some keyboard/mouses. Often referred as \"Browser Back\"\n    ImGuiKey_AppForward,\n\n    // Gamepad (some of those are analog values, 0.0f to 1.0f)                          // NAVIGATION ACTION\n    // (download controller mapping PNG/PSD at http://dearimgui.com/controls_sheets)\n    ImGuiKey_GamepadStart,          // Menu (Xbox)      + (Switch)   Start/Options (PS)\n    ImGuiKey_GamepadBack,           // View (Xbox)      - (Switch)   Share (PS)\n    ImGuiKey_GamepadFaceLeft,       // X (Xbox)         Y (Switch)   Square (PS)        // Tap: Toggle Menu. Hold: Windowing mode (Focus/Move/Resize windows)\n    ImGuiKey_GamepadFaceRight,      // B (Xbox)         A (Switch)   Circle (PS)        // Cancel / Close / Exit\n    ImGuiKey_GamepadFaceUp,         // Y (Xbox)         X (Switch)   Triangle (PS)      // Text Input / On-screen Keyboard\n    ImGuiKey_GamepadFaceDown,       // A (Xbox)         B (Switch)   Cross (PS)         // Activate / Open / Toggle / Tweak\n    ImGuiKey_GamepadDpadLeft,       // D-pad Left                                       // Move / Tweak / Resize Window (in Windowing mode)\n    ImGuiKey_GamepadDpadRight,      // D-pad Right                                      // Move / Tweak / Resize Window (in Windowing mode)\n    ImGuiKey_GamepadDpadUp,         // D-pad Up                                         // Move / Tweak / Resize Window (in Windowing mode)\n    ImGuiKey_GamepadDpadDown,       // D-pad Down                                       // Move / Tweak / Resize Window (in Windowing mode)\n    ImGuiKey_GamepadL1,             // L Bumper (Xbox)  L (Switch)   L1 (PS)            // Tweak Slower / Focus Previous (in Windowing mode)\n    ImGuiKey_GamepadR1,             // R Bumper (Xbox)  R (Switch)   R1 (PS)            // Tweak Faster / Focus Next (in Windowing mode)\n    ImGuiKey_GamepadL2,             // L Trig. (Xbox)   ZL (Switch)  L2 (PS) [Analog]\n    ImGuiKey_GamepadR2,             // R Trig. (Xbox)   ZR (Switch)  R2 (PS) [Analog]\n    ImGuiKey_GamepadL3,             // L Stick (Xbox)   L3 (Switch)  L3 (PS)\n    ImGuiKey_GamepadR3,             // R Stick (Xbox)   R3 (Switch)  R3 (PS)\n    ImGuiKey_GamepadLStickLeft,     // [Analog]                                         // Move Window (in Windowing mode)\n    ImGuiKey_GamepadLStickRight,    // [Analog]                                         // Move Window (in Windowing mode)\n    ImGuiKey_GamepadLStickUp,       // [Analog]                                         // Move Window (in Windowing mode)\n    ImGuiKey_GamepadLStickDown,     // [Analog]                                         // Move Window (in Windowing mode)\n    ImGuiKey_GamepadRStickLeft,     // [Analog]\n    ImGuiKey_GamepadRStickRight,    // [Analog]\n    ImGuiKey_GamepadRStickUp,       // [Analog]\n    ImGuiKey_GamepadRStickDown,     // [Analog]\n\n    // Aliases: Mouse Buttons (auto-submitted from AddMouseButtonEvent() calls)\n    // - This is mirroring the data also written to io.MouseDown[], io.MouseWheel, in a format allowing them to be accessed via standard key API.\n    ImGuiKey_MouseLeft, ImGuiKey_MouseRight, ImGuiKey_MouseMiddle, ImGuiKey_MouseX1, ImGuiKey_MouseX2, ImGuiKey_MouseWheelX, ImGuiKey_MouseWheelY,\n\n    // [Internal] Reserved for mod storage\n    ImGuiKey_ReservedForModCtrl, ImGuiKey_ReservedForModShift, ImGuiKey_ReservedForModAlt, ImGuiKey_ReservedForModSuper,\n    ImGuiKey_COUNT,\n\n    // Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls)\n    // - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing\n    //   them to be accessed via standard key API, allowing calls such as IsKeyPressed(), IsKeyReleased(), querying duration etc.\n    // - Code polling every key (e.g. an interface to detect a key press for input mapping) might want to ignore those\n    //   and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiMod_Ctrl).\n    // - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys.\n    //   In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and\n    //   backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user...\n    ImGuiMod_None                   = 0,\n    ImGuiMod_Ctrl                   = 1 << 12, // Ctrl\n    ImGuiMod_Shift                  = 1 << 13, // Shift\n    ImGuiMod_Alt                    = 1 << 14, // Option/Menu\n    ImGuiMod_Super                  = 1 << 15, // Cmd/Super/Windows\n    ImGuiMod_Shortcut               = 1 << 11, // Alias for Ctrl (non-macOS) _or_ Super (macOS).\n    ImGuiMod_Mask_                  = 0xF800,  // 5-bits\n\n    // [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + the io.KeyMap[] array.\n    // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE)\n    // If you need to iterate all keys (for e.g. an input mapper) you may use ImGuiKey_NamedKey_BEGIN..ImGuiKey_NamedKey_END.\n    ImGuiKey_NamedKey_BEGIN         = 512,\n    ImGuiKey_NamedKey_END           = ImGuiKey_COUNT,\n    ImGuiKey_NamedKey_COUNT         = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN,\n#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO\n    ImGuiKey_KeysData_SIZE          = ImGuiKey_NamedKey_COUNT,  // Size of KeysData[]: only hold named keys\n    ImGuiKey_KeysData_OFFSET        = ImGuiKey_NamedKey_BEGIN,  // Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET) index.\n#else\n    ImGuiKey_KeysData_SIZE          = ImGuiKey_COUNT,           // Size of KeysData[]: hold legacy 0..512 keycodes + named keys\n    ImGuiKey_KeysData_OFFSET        = 0,                        // Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET) index.\n#endif\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89\n    ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter,    // Renamed in 1.87\n#endif\n};\n\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n// OBSOLETED in 1.88 (from July 2022): ImGuiNavInput and io.NavInputs[].\n// Official backends between 1.60 and 1.86: will keep working and feed gamepad inputs as long as IMGUI_DISABLE_OBSOLETE_KEYIO is not set.\n// Custom backends: feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums.\nenum ImGuiNavInput\n{\n    ImGuiNavInput_Activate, ImGuiNavInput_Cancel, ImGuiNavInput_Input, ImGuiNavInput_Menu, ImGuiNavInput_DpadLeft, ImGuiNavInput_DpadRight, ImGuiNavInput_DpadUp, ImGuiNavInput_DpadDown,\n    ImGuiNavInput_LStickLeft, ImGuiNavInput_LStickRight, ImGuiNavInput_LStickUp, ImGuiNavInput_LStickDown, ImGuiNavInput_FocusPrev, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakSlow, ImGuiNavInput_TweakFast,\n    ImGuiNavInput_COUNT,\n};\n#endif\n\n// Configuration flags stored in io.ConfigFlags. Set by user/application.\nenum ImGuiConfigFlags_\n{\n    ImGuiConfigFlags_None                   = 0,\n    ImGuiConfigFlags_NavEnableKeyboard      = 1 << 0,   // Master keyboard navigation enable flag. Enable full Tabbing + directional arrows + space/enter to activate.\n    ImGuiConfigFlags_NavEnableGamepad       = 1 << 1,   // Master gamepad navigation enable flag. Backend also needs to set ImGuiBackendFlags_HasGamepad.\n    ImGuiConfigFlags_NavEnableSetMousePos   = 1 << 2,   // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your backend, otherwise ImGui will react as if the mouse is jumping around back and forth.\n    ImGuiConfigFlags_NavNoCaptureKeyboard   = 1 << 3,   // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set.\n    ImGuiConfigFlags_NoMouse                = 1 << 4,   // Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information set by the backend.\n    ImGuiConfigFlags_NoMouseCursorChange    = 1 << 5,   // Instruct backend to not alter mouse cursor shape and visibility. Use if the backend cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead.\n\n    // User storage (to allow your backend/engine to communicate to code that may be shared between multiple projects. Those flags are NOT used by core Dear ImGui)\n    ImGuiConfigFlags_IsSRGB                 = 1 << 20,  // Application is SRGB-aware.\n    ImGuiConfigFlags_IsTouchScreen          = 1 << 21,  // Application is using a touch screen instead of a mouse.\n};\n\n// Backend capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom backend.\nenum ImGuiBackendFlags_\n{\n    ImGuiBackendFlags_None                  = 0,\n    ImGuiBackendFlags_HasGamepad            = 1 << 0,   // Backend Platform supports gamepad and currently has one connected.\n    ImGuiBackendFlags_HasMouseCursors       = 1 << 1,   // Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape.\n    ImGuiBackendFlags_HasSetMousePos        = 1 << 2,   // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if ImGuiConfigFlags_NavEnableSetMousePos is set).\n    ImGuiBackendFlags_RendererHasVtxOffset  = 1 << 3,   // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices.\n};\n\n// Enumeration for PushStyleColor() / PopStyleColor()\nenum ImGuiCol_\n{\n    ImGuiCol_Text,\n    ImGuiCol_TextDisabled,\n    ImGuiCol_WindowBg,              // Background of normal windows\n    ImGuiCol_ChildBg,               // Background of child windows\n    ImGuiCol_PopupBg,               // Background of popups, menus, tooltips windows\n    ImGuiCol_Border,\n    ImGuiCol_BorderShadow,\n    ImGuiCol_FrameBg,               // Background of checkbox, radio button, plot, slider, text input\n    ImGuiCol_FrameBgHovered,\n    ImGuiCol_FrameBgActive,\n    ImGuiCol_TitleBg,\n    ImGuiCol_TitleBgActive,\n    ImGuiCol_TitleBgCollapsed,\n    ImGuiCol_MenuBarBg,\n    ImGuiCol_ScrollbarBg,\n    ImGuiCol_ScrollbarGrab,\n    ImGuiCol_ScrollbarGrabHovered,\n    ImGuiCol_ScrollbarGrabActive,\n    ImGuiCol_CheckMark,\n    ImGuiCol_SliderGrab,\n    ImGuiCol_SliderGrabActive,\n    ImGuiCol_Button,\n    ImGuiCol_ButtonHovered,\n    ImGuiCol_ButtonActive,\n    ImGuiCol_Header,                // Header* colors are used for CollapsingHeader, TreeNode, Selectable, MenuItem\n    ImGuiCol_HeaderHovered,\n    ImGuiCol_HeaderActive,\n    ImGuiCol_Separator,\n    ImGuiCol_SeparatorHovered,\n    ImGuiCol_SeparatorActive,\n    ImGuiCol_ResizeGrip,            // Resize grip in lower-right and lower-left corners of windows.\n    ImGuiCol_ResizeGripHovered,\n    ImGuiCol_ResizeGripActive,\n    ImGuiCol_Tab,                   // TabItem in a TabBar\n    ImGuiCol_TabHovered,\n    ImGuiCol_TabActive,\n    ImGuiCol_TabUnfocused,\n    ImGuiCol_TabUnfocusedActive,\n    ImGuiCol_PlotLines,\n    ImGuiCol_PlotLinesHovered,\n    ImGuiCol_PlotHistogram,\n    ImGuiCol_PlotHistogramHovered,\n    ImGuiCol_TableHeaderBg,         // Table header background\n    ImGuiCol_TableBorderStrong,     // Table outer and header borders (prefer using Alpha=1.0 here)\n    ImGuiCol_TableBorderLight,      // Table inner borders (prefer using Alpha=1.0 here)\n    ImGuiCol_TableRowBg,            // Table row background (even rows)\n    ImGuiCol_TableRowBgAlt,         // Table row background (odd rows)\n    ImGuiCol_TextSelectedBg,\n    ImGuiCol_DragDropTarget,        // Rectangle highlighting a drop target\n    ImGuiCol_NavHighlight,          // Gamepad/keyboard: current highlighted item\n    ImGuiCol_NavWindowingHighlight, // Highlight window when using CTRL+TAB\n    ImGuiCol_NavWindowingDimBg,     // Darken/colorize entire screen behind the CTRL+TAB window list, when active\n    ImGuiCol_ModalWindowDimBg,      // Darken/colorize entire screen behind a modal window, when one is active\n    ImGuiCol_COUNT\n};\n\n// Enumeration for PushStyleVar() / PopStyleVar() to temporarily modify the ImGuiStyle structure.\n// - The enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code.\n//   During initialization or between frames, feel free to just poke into ImGuiStyle directly.\n// - Tip: Use your programming IDE navigation facilities on the names in the _second column_ below to find the actual members and their description.\n//   In Visual Studio IDE: CTRL+comma (\"Edit.GoToAll\") can follow symbols in comments, whereas CTRL+F12 (\"Edit.GoToImplementation\") cannot.\n//   With Visual Assist installed: ALT+G (\"VAssistX.GoToImplementation\") can also follow symbols in comments.\n// - When changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type.\nenum ImGuiStyleVar_\n{\n    // Enum name --------------------- // Member in ImGuiStyle structure (see ImGuiStyle for descriptions)\n    ImGuiStyleVar_Alpha,               // float     Alpha\n    ImGuiStyleVar_DisabledAlpha,       // float     DisabledAlpha\n    ImGuiStyleVar_WindowPadding,       // ImVec2    WindowPadding\n    ImGuiStyleVar_WindowRounding,      // float     WindowRounding\n    ImGuiStyleVar_WindowBorderSize,    // float     WindowBorderSize\n    ImGuiStyleVar_WindowMinSize,       // ImVec2    WindowMinSize\n    ImGuiStyleVar_WindowTitleAlign,    // ImVec2    WindowTitleAlign\n    ImGuiStyleVar_ChildRounding,       // float     ChildRounding\n    ImGuiStyleVar_ChildBorderSize,     // float     ChildBorderSize\n    ImGuiStyleVar_PopupRounding,       // float     PopupRounding\n    ImGuiStyleVar_PopupBorderSize,     // float     PopupBorderSize\n    ImGuiStyleVar_FramePadding,        // ImVec2    FramePadding\n    ImGuiStyleVar_FrameRounding,       // float     FrameRounding\n    ImGuiStyleVar_FrameBorderSize,     // float     FrameBorderSize\n    ImGuiStyleVar_ItemSpacing,         // ImVec2    ItemSpacing\n    ImGuiStyleVar_ItemInnerSpacing,    // ImVec2    ItemInnerSpacing\n    ImGuiStyleVar_IndentSpacing,       // float     IndentSpacing\n    ImGuiStyleVar_CellPadding,         // ImVec2    CellPadding\n    ImGuiStyleVar_ScrollbarSize,       // float     ScrollbarSize\n    ImGuiStyleVar_ScrollbarRounding,   // float     ScrollbarRounding\n    ImGuiStyleVar_GrabMinSize,         // float     GrabMinSize\n    ImGuiStyleVar_GrabRounding,        // float     GrabRounding\n    ImGuiStyleVar_TabRounding,         // float     TabRounding\n    ImGuiStyleVar_TabBarBorderSize,    // float     TabBarBorderSize\n    ImGuiStyleVar_ButtonTextAlign,     // ImVec2    ButtonTextAlign\n    ImGuiStyleVar_SelectableTextAlign, // ImVec2    SelectableTextAlign\n    ImGuiStyleVar_SeparatorTextBorderSize,// float  SeparatorTextBorderSize\n    ImGuiStyleVar_SeparatorTextAlign,  // ImVec2    SeparatorTextAlign\n    ImGuiStyleVar_SeparatorTextPadding,// ImVec2    SeparatorTextPadding\n    ImGuiStyleVar_COUNT\n};\n\n// Flags for InvisibleButton() [extended in imgui_internal.h]\nenum ImGuiButtonFlags_\n{\n    ImGuiButtonFlags_None                   = 0,\n    ImGuiButtonFlags_MouseButtonLeft        = 1 << 0,   // React on left mouse button (default)\n    ImGuiButtonFlags_MouseButtonRight       = 1 << 1,   // React on right mouse button\n    ImGuiButtonFlags_MouseButtonMiddle      = 1 << 2,   // React on center mouse button\n\n    // [Internal]\n    ImGuiButtonFlags_MouseButtonMask_       = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle,\n    ImGuiButtonFlags_MouseButtonDefault_    = ImGuiButtonFlags_MouseButtonLeft,\n};\n\n// Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton()\nenum ImGuiColorEditFlags_\n{\n    ImGuiColorEditFlags_None            = 0,\n    ImGuiColorEditFlags_NoAlpha         = 1 << 1,   //              // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (will only read 3 components from the input pointer).\n    ImGuiColorEditFlags_NoPicker        = 1 << 2,   //              // ColorEdit: disable picker when clicking on color square.\n    ImGuiColorEditFlags_NoOptions       = 1 << 3,   //              // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview.\n    ImGuiColorEditFlags_NoSmallPreview  = 1 << 4,   //              // ColorEdit, ColorPicker: disable color square preview next to the inputs. (e.g. to show only the inputs)\n    ImGuiColorEditFlags_NoInputs        = 1 << 5,   //              // ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the small preview color square).\n    ImGuiColorEditFlags_NoTooltip       = 1 << 6,   //              // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview.\n    ImGuiColorEditFlags_NoLabel         = 1 << 7,   //              // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker).\n    ImGuiColorEditFlags_NoSidePreview   = 1 << 8,   //              // ColorPicker: disable bigger color preview on right side of the picker, use small color square preview instead.\n    ImGuiColorEditFlags_NoDragDrop      = 1 << 9,   //              // ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source.\n    ImGuiColorEditFlags_NoBorder        = 1 << 10,  //              // ColorButton: disable border (which is enforced by default)\n\n    // User Options (right-click on widget to change some of them).\n    ImGuiColorEditFlags_AlphaBar        = 1 << 16,  //              // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker.\n    ImGuiColorEditFlags_AlphaPreview    = 1 << 17,  //              // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque.\n    ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 18,  //              // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead of opaque.\n    ImGuiColorEditFlags_HDR             = 1 << 19,  //              // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well).\n    ImGuiColorEditFlags_DisplayRGB      = 1 << 20,  // [Display]    // ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex.\n    ImGuiColorEditFlags_DisplayHSV      = 1 << 21,  // [Display]    // \"\n    ImGuiColorEditFlags_DisplayHex      = 1 << 22,  // [Display]    // \"\n    ImGuiColorEditFlags_Uint8           = 1 << 23,  // [DataType]   // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255.\n    ImGuiColorEditFlags_Float           = 1 << 24,  // [DataType]   // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers.\n    ImGuiColorEditFlags_PickerHueBar    = 1 << 25,  // [Picker]     // ColorPicker: bar for Hue, rectangle for Sat/Value.\n    ImGuiColorEditFlags_PickerHueWheel  = 1 << 26,  // [Picker]     // ColorPicker: wheel for Hue, triangle for Sat/Value.\n    ImGuiColorEditFlags_InputRGB        = 1 << 27,  // [Input]      // ColorEdit, ColorPicker: input and output data in RGB format.\n    ImGuiColorEditFlags_InputHSV        = 1 << 28,  // [Input]      // ColorEdit, ColorPicker: input and output data in HSV format.\n\n    // Defaults Options. You can set application defaults using SetColorEditOptions(). The intent is that you probably don't want to\n    // override them in most of your calls. Let the user choose via the option menu and/or call SetColorEditOptions() once during startup.\n    ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar,\n\n    // [Internal] Masks\n    ImGuiColorEditFlags_DisplayMask_    = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex,\n    ImGuiColorEditFlags_DataTypeMask_   = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float,\n    ImGuiColorEditFlags_PickerMask_     = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar,\n    ImGuiColorEditFlags_InputMask_      = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV,\n\n    // Obsolete names\n    //ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex  // [renamed in 1.69]\n};\n\n// Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc.\n// We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them.\n// (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigDragClickToInputText)\nenum ImGuiSliderFlags_\n{\n    ImGuiSliderFlags_None                   = 0,\n    ImGuiSliderFlags_AlwaysClamp            = 1 << 4,       // Clamp value to min/max bounds when input manually with CTRL+Click. By default CTRL+Click allows going out of bounds.\n    ImGuiSliderFlags_Logarithmic            = 1 << 5,       // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits.\n    ImGuiSliderFlags_NoRoundToFormat        = 1 << 6,       // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits)\n    ImGuiSliderFlags_NoInput                = 1 << 7,       // Disable CTRL+Click or Enter key allowing to input text directly into the widget\n    ImGuiSliderFlags_InvalidMask_           = 0x7000000F,   // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed.\n\n    // Obsolete names\n    //ImGuiSliderFlags_ClampOnInput = ImGuiSliderFlags_AlwaysClamp, // [renamed in 1.79]\n};\n\n// Identify a mouse button.\n// Those values are guaranteed to be stable and we frequently use 0/1 directly. Named enums provided for convenience.\nenum ImGuiMouseButton_\n{\n    ImGuiMouseButton_Left = 0,\n    ImGuiMouseButton_Right = 1,\n    ImGuiMouseButton_Middle = 2,\n    ImGuiMouseButton_COUNT = 5\n};\n\n// Enumeration for GetMouseCursor()\n// User code may request backend to display given cursor by calling SetMouseCursor(), which is why we have some cursors that are marked unused here\nenum ImGuiMouseCursor_\n{\n    ImGuiMouseCursor_None = -1,\n    ImGuiMouseCursor_Arrow = 0,\n    ImGuiMouseCursor_TextInput,         // When hovering over InputText, etc.\n    ImGuiMouseCursor_ResizeAll,         // (Unused by Dear ImGui functions)\n    ImGuiMouseCursor_ResizeNS,          // When hovering over a horizontal border\n    ImGuiMouseCursor_ResizeEW,          // When hovering over a vertical border or a column\n    ImGuiMouseCursor_ResizeNESW,        // When hovering over the bottom-left corner of a window\n    ImGuiMouseCursor_ResizeNWSE,        // When hovering over the bottom-right corner of a window\n    ImGuiMouseCursor_Hand,              // (Unused by Dear ImGui functions. Use for e.g. hyperlinks)\n    ImGuiMouseCursor_NotAllowed,        // When hovering something with disallowed interaction. Usually a crossed circle.\n    ImGuiMouseCursor_COUNT\n};\n\n// Enumeration for AddMouseSourceEvent() actual source of Mouse Input data.\n// Historically we use \"Mouse\" terminology everywhere to indicate pointer data, e.g. MousePos, IsMousePressed(), io.AddMousePosEvent()\n// But that \"Mouse\" data can come from different source which occasionally may be useful for application to know about.\n// You can submit a change of pointer type using io.AddMouseSourceEvent().\nenum ImGuiMouseSource : int\n{\n    ImGuiMouseSource_Mouse = 0,         // Input is coming from an actual mouse.\n    ImGuiMouseSource_TouchScreen,       // Input is coming from a touch screen (no hovering prior to initial press, less precise initial press aiming, dual-axis wheeling possible).\n    ImGuiMouseSource_Pen,               // Input is coming from a pressure/magnetic pen (often used in conjunction with high-sampling rates).\n    ImGuiMouseSource_COUNT\n};\n\n// Enumeration for ImGui::SetWindow***(), SetNextWindow***(), SetNextItem***() functions\n// Represent a condition.\n// Important: Treat as a regular enum! Do NOT combine multiple values using binary operators! All the functions above treat 0 as a shortcut to ImGuiCond_Always.\nenum ImGuiCond_\n{\n    ImGuiCond_None          = 0,        // No condition (always set the variable), same as _Always\n    ImGuiCond_Always        = 1 << 0,   // No condition (always set the variable), same as _None\n    ImGuiCond_Once          = 1 << 1,   // Set the variable once per runtime session (only the first call will succeed)\n    ImGuiCond_FirstUseEver  = 1 << 2,   // Set the variable if the object/window has no persistently saved data (no entry in .ini file)\n    ImGuiCond_Appearing     = 1 << 3,   // Set the variable if the object/window is appearing after being hidden/inactive (or the first time)\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Helpers: Memory allocations macros, ImVector<>\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// IM_MALLOC(), IM_FREE(), IM_NEW(), IM_PLACEMENT_NEW(), IM_DELETE()\n// We call C++ constructor on own allocated memory via the placement \"new(ptr) Type()\" syntax.\n// Defining a custom placement new() with a custom parameter allows us to bypass including <new> which on some platforms complains when user has disabled exceptions.\n//-----------------------------------------------------------------------------\n\nstruct ImNewWrapper {};\ninline void* operator new(size_t, ImNewWrapper, void* ptr) { return ptr; }\ninline void  operator delete(void*, ImNewWrapper, void*)   {} // This is only required so we can use the symmetrical new()\n#define IM_ALLOC(_SIZE)                     ImGui::MemAlloc(_SIZE)\n#define IM_FREE(_PTR)                       ImGui::MemFree(_PTR)\n#define IM_PLACEMENT_NEW(_PTR)              new(ImNewWrapper(), _PTR)\n#define IM_NEW(_TYPE)                       new(ImNewWrapper(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE\ntemplate<typename T> void IM_DELETE(T* p)   { if (p) { p->~T(); ImGui::MemFree(p); } }\n\n//-----------------------------------------------------------------------------\n// ImVector<>\n// Lightweight std::vector<>-like class to avoid dragging dependencies (also, some implementations of STL with debug enabled are absurdly slow, we bypass it so our code runs fast in debug).\n//-----------------------------------------------------------------------------\n// - You generally do NOT need to care or use this ever. But we need to make it available in imgui.h because some of our public structures are relying on it.\n// - We use std-like naming convention here, which is a little unusual for this codebase.\n// - Important: clear() frees memory, resize(0) keep the allocated buffer. We use resize(0) a lot to intentionally recycle allocated buffers across frames and amortize our costs.\n// - Important: our implementation does NOT call C++ constructors/destructors, we treat everything as raw data! This is intentional but be extra mindful of that,\n//   Do NOT use this class as a std::vector replacement in your own code! Many of the structures used by dear imgui can be safely initialized by a zero-memset.\n//-----------------------------------------------------------------------------\n\nIM_MSVC_RUNTIME_CHECKS_OFF\ntemplate<typename T>\nstruct ImVector\n{\n    int                 Size;\n    int                 Capacity;\n    T*                  Data;\n\n    // Provide standard typedefs but we don't use them ourselves.\n    typedef T                   value_type;\n    typedef value_type*         iterator;\n    typedef const value_type*   const_iterator;\n\n    // Constructors, destructor\n    inline ImVector()                                       { Size = Capacity = 0; Data = NULL; }\n    inline ImVector(const ImVector<T>& src)                 { Size = Capacity = 0; Data = NULL; operator=(src); }\n    inline ImVector<T>& operator=(const ImVector<T>& src)   { clear(); resize(src.Size); if (src.Data) memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; }\n    inline ~ImVector()                                      { if (Data) IM_FREE(Data); } // Important: does not destruct anything\n\n    inline void         clear()                             { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } }  // Important: does not destruct anything\n    inline void         clear_delete()                      { for (int n = 0; n < Size; n++) IM_DELETE(Data[n]); clear(); }     // Important: never called automatically! always explicit.\n    inline void         clear_destruct()                    { for (int n = 0; n < Size; n++) Data[n].~T(); clear(); }           // Important: never called automatically! always explicit.\n\n    inline bool         empty() const                       { return Size == 0; }\n    inline int          size() const                        { return Size; }\n    inline int          size_in_bytes() const               { return Size * (int)sizeof(T); }\n    inline int          max_size() const                    { return 0x7FFFFFFF / (int)sizeof(T); }\n    inline int          capacity() const                    { return Capacity; }\n    inline T&           operator[](int i)                   { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }\n    inline const T&     operator[](int i) const             { IM_ASSERT(i >= 0 && i < Size); return Data[i]; }\n\n    inline T*           begin()                             { return Data; }\n    inline const T*     begin() const                       { return Data; }\n    inline T*           end()                               { return Data + Size; }\n    inline const T*     end() const                         { return Data + Size; }\n    inline T&           front()                             { IM_ASSERT(Size > 0); return Data[0]; }\n    inline const T&     front() const                       { IM_ASSERT(Size > 0); return Data[0]; }\n    inline T&           back()                              { IM_ASSERT(Size > 0); return Data[Size - 1]; }\n    inline const T&     back() const                        { IM_ASSERT(Size > 0); return Data[Size - 1]; }\n    inline void         swap(ImVector<T>& rhs)              { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; T* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; }\n\n    inline int          _grow_capacity(int sz) const        { int new_capacity = Capacity ? (Capacity + Capacity / 2) : 8; return new_capacity > sz ? new_capacity : sz; }\n    inline void         resize(int new_size)                { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; }\n    inline void         resize(int new_size, const T& v)    { if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; }\n    inline void         shrink(int new_size)                { IM_ASSERT(new_size <= Size); Size = new_size; } // Resize a vector to a smaller size, guaranteed not to cause a reallocation\n    inline void         reserve(int new_capacity)           { if (new_capacity <= Capacity) return; T* new_data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); if (Data) { memcpy(new_data, Data, (size_t)Size * sizeof(T)); IM_FREE(Data); } Data = new_data; Capacity = new_capacity; }\n    inline void         reserve_discard(int new_capacity)   { if (new_capacity <= Capacity) return; if (Data) IM_FREE(Data); Data = (T*)IM_ALLOC((size_t)new_capacity * sizeof(T)); Capacity = new_capacity; }\n\n    // NB: It is illegal to call push_back/push_front/insert with a reference pointing inside the ImVector data itself! e.g. v.push_back(v[10]) is forbidden.\n    inline void         push_back(const T& v)               { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; }\n    inline void         pop_back()                          { IM_ASSERT(Size > 0); Size--; }\n    inline void         push_front(const T& v)              { if (Size == 0) push_back(v); else insert(Data, v); }\n    inline T*           erase(const T* it)                  { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(T)); Size--; return Data + off; }\n    inline T*           erase(const T* it, const T* it_last){ IM_ASSERT(it >= Data && it < Data + Size && it_last >= it && it_last <= Data + Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - (size_t)count) * sizeof(T)); Size -= (int)count; return Data + off; }\n    inline T*           erase_unsorted(const T* it)         { IM_ASSERT(it >= Data && it < Data + Size);  const ptrdiff_t off = it - Data; if (it < Data + Size - 1) memcpy(Data + off, Data + Size - 1, sizeof(T)); Size--; return Data + off; }\n    inline T*           insert(const T* it, const T& v)     { IM_ASSERT(it >= Data && it <= Data + Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(T)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; }\n    inline bool         contains(const T& v) const          { const T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; }\n    inline T*           find(const T& v)                    { T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }\n    inline const T*     find(const T& v) const              { const T* data = Data;  const T* data_end = Data + Size; while (data < data_end) if (*data == v) break; else ++data; return data; }\n    inline int          find_index(const T& v) const        { const T* data_end = Data + Size; const T* it = find(v); if (it == data_end) return -1; const ptrdiff_t off = it - Data; return (int)off; }\n    inline bool         find_erase(const T& v)              { const T* it = find(v); if (it < Data + Size) { erase(it); return true; } return false; }\n    inline bool         find_erase_unsorted(const T& v)     { const T* it = find(v); if (it < Data + Size) { erase_unsorted(it); return true; } return false; }\n    inline int          index_from_ptr(const T* it) const   { IM_ASSERT(it >= Data && it < Data + Size); const ptrdiff_t off = it - Data; return (int)off; }\n};\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiStyle\n//-----------------------------------------------------------------------------\n// You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame().\n// During the frame, use ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values,\n// and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors.\n//-----------------------------------------------------------------------------\n\nstruct ImGuiStyle\n{\n    float       Alpha;                      // Global alpha applies to everything in Dear ImGui.\n    float       DisabledAlpha;              // Additional alpha multiplier applied by BeginDisabled(). Multiply over current value of Alpha.\n    ImVec2      WindowPadding;              // Padding within a window.\n    float       WindowRounding;             // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended.\n    float       WindowBorderSize;           // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).\n    ImVec2      WindowMinSize;              // Minimum window size. This is a global setting. If you want to constrain individual windows, use SetNextWindowSizeConstraints().\n    ImVec2      WindowTitleAlign;           // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered.\n    ImGuiDir    WindowMenuButtonPosition;   // Side of the collapsing/docking button in the title bar (None/Left/Right). Defaults to ImGuiDir_Left.\n    float       ChildRounding;              // Radius of child window corners rounding. Set to 0.0f to have rectangular windows.\n    float       ChildBorderSize;            // Thickness of border around child windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).\n    float       PopupRounding;              // Radius of popup window corners rounding. (Note that tooltip windows use WindowRounding)\n    float       PopupBorderSize;            // Thickness of border around popup/tooltip windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).\n    ImVec2      FramePadding;               // Padding within a framed rectangle (used by most widgets).\n    float       FrameRounding;              // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets).\n    float       FrameBorderSize;            // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly).\n    ImVec2      ItemSpacing;                // Horizontal and vertical spacing between widgets/lines.\n    ImVec2      ItemInnerSpacing;           // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label).\n    ImVec2      CellPadding;                // Padding within a table cell. CellPadding.y may be altered between different rows.\n    ImVec2      TouchExtraPadding;          // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much!\n    float       IndentSpacing;              // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2).\n    float       ColumnsMinSpacing;          // Minimum horizontal spacing between two columns. Preferably > (FramePadding.x + 1).\n    float       ScrollbarSize;              // Width of the vertical scrollbar, Height of the horizontal scrollbar.\n    float       ScrollbarRounding;          // Radius of grab corners for scrollbar.\n    float       GrabMinSize;                // Minimum width/height of a grab box for slider/scrollbar.\n    float       GrabRounding;               // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs.\n    float       LogSliderDeadzone;          // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero.\n    float       TabRounding;                // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs.\n    float       TabBorderSize;              // Thickness of border around tabs.\n    float       TabMinWidthForCloseButton;  // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected.\n    float       TabBarBorderSize;           // Thickness of tab-bar separator, which takes on the tab active color to denote focus.\n    float       TableAngledHeadersAngle;    // Angle of angled headers (supported values range from -50.0f degrees to +50.0f degrees).\n    ImGuiDir    ColorButtonPosition;        // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right.\n    ImVec2      ButtonTextAlign;            // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered).\n    ImVec2      SelectableTextAlign;        // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line.\n    float       SeparatorTextBorderSize;    // Thickkness of border in SeparatorText()\n    ImVec2      SeparatorTextAlign;         // Alignment of text within the separator. Defaults to (0.0f, 0.5f) (left aligned, center).\n    ImVec2      SeparatorTextPadding;       // Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y.\n    ImVec2      DisplayWindowPadding;       // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows.\n    ImVec2      DisplaySafeAreaPadding;     // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly!\n    float       MouseCursorScale;           // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later.\n    bool        AntiAliasedLines;           // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).\n    bool        AntiAliasedLinesUseTex;     // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). Latched at the beginning of the frame (copied to ImDrawList).\n    bool        AntiAliasedFill;            // Enable anti-aliased edges around filled shapes (rounded rectangles, circles, etc.). Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList).\n    float       CurveTessellationTol;       // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.\n    float       CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.\n    ImVec4      Colors[ImGuiCol_COUNT];\n\n    // Behaviors\n    // (It is possible to modify those fields mid-frame if specific behavior need it, unlike e.g. configuration fields in ImGuiIO)\n    float             HoverStationaryDelay;     // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.\n    float             HoverDelayShort;          // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.\n    float             HoverDelayNormal;         // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). \"\n    ImGuiHoveredFlags HoverFlagsForTooltipMouse;// Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.\n    ImGuiHoveredFlags HoverFlagsForTooltipNav;  // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.\n\n    IMGUI_API ImGuiStyle();\n    IMGUI_API void ScaleAllSizes(float scale_factor);\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiIO\n//-----------------------------------------------------------------------------\n// Communicate most settings and inputs/outputs to Dear ImGui using this structure.\n// Access via ImGui::GetIO(). Read 'Programmer guide' section in .cpp file for general usage.\n//-----------------------------------------------------------------------------\n\n// [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions.\n// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and *NOT* io.KeysData[key]->DownDuration.\nstruct ImGuiKeyData\n{\n    bool        Down;               // True for if key is down\n    float       DownDuration;       // Duration the key has been down (<0.0f: not pressed, 0.0f: just pressed, >0.0f: time held)\n    float       DownDurationPrev;   // Last frame duration the key has been down\n    float       AnalogValue;        // 0.0f..1.0f for gamepad values\n};\n\nstruct ImGuiIO\n{\n    //------------------------------------------------------------------\n    // Configuration                            // Default value\n    //------------------------------------------------------------------\n\n    ImGuiConfigFlags   ConfigFlags;             // = 0              // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc.\n    ImGuiBackendFlags  BackendFlags;            // = 0              // See ImGuiBackendFlags_ enum. Set by backend (imgui_impl_xxx files or custom backend) to communicate features supported by the backend.\n    ImVec2      DisplaySize;                    // <unset>          // Main display size, in pixels (generally == GetMainViewport()->Size). May change every frame.\n    float       DeltaTime;                      // = 1.0f/60.0f     // Time elapsed since last frame, in seconds. May change every frame.\n    float       IniSavingRate;                  // = 5.0f           // Minimum time between saving positions/sizes to .ini file, in seconds.\n    const char* IniFilename;                    // = \"imgui.ini\"    // Path to .ini file (important: default \"imgui.ini\" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions.\n    const char* LogFilename;                    // = \"imgui_log.txt\"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified).\n    void*       UserData;                       // = NULL           // Store your own data.\n\n    ImFontAtlas*Fonts;                          // <auto>           // Font atlas: load, rasterize and pack one or more fonts into a single texture.\n    float       FontGlobalScale;                // = 1.0f           // Global scale all fonts\n    bool        FontAllowUserScaling;           // = false          // Allow user scaling text of individual window with CTRL+Wheel.\n    ImFont*     FontDefault;                    // = NULL           // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0].\n    ImVec2      DisplayFramebufferScale;        // = (1, 1)         // For retina display or other situations where window coordinates are different from framebuffer coordinates. This generally ends up in ImDrawData::FramebufferScale.\n\n    // Miscellaneous options\n    bool        MouseDrawCursor;                // = false          // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations.\n    bool        ConfigMacOSXBehaviors;          // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl.\n    bool        ConfigInputTrickleEventQueue;   // = true           // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.\n    bool        ConfigInputTextCursorBlink;     // = true           // Enable blinking cursor (optional as some users consider it to be distracting).\n    bool        ConfigInputTextEnterKeepActive; // = false          // [BETA] Pressing Enter will keep item active and select contents (single-line only).\n    bool        ConfigDragClickToInputText;     // = false          // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard.\n    bool        ConfigWindowsResizeFromEdges;   // = true           // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag)\n    bool        ConfigWindowsMoveFromTitleBarOnly; // = false       // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar.\n    float       ConfigMemoryCompactTimer;       // = 60.0f          // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable.\n\n    // Inputs Behaviors\n    // (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle)\n    float       MouseDoubleClickTime;           // = 0.30f          // Time for a double-click, in seconds.\n    float       MouseDoubleClickMaxDist;        // = 6.0f           // Distance threshold to stay in to validate a double-click, in pixels.\n    float       MouseDragThreshold;             // = 6.0f           // Distance threshold before considering we are dragging.\n    float       KeyRepeatDelay;                 // = 0.275f         // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.).\n    float       KeyRepeatRate;                  // = 0.050f         // When holding a key/button, rate at which it repeats, in seconds.\n\n    //------------------------------------------------------------------\n    // Debug options\n    //------------------------------------------------------------------\n\n    // Tools to test correct Begin/End and BeginChild/EndChild behaviors.\n    // Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX()\n    // This is inconsistent with other BeginXXX functions and create confusion for many users.\n    // We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior.\n    bool        ConfigDebugBeginReturnValueOnce;// = false          // First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows.\n    bool        ConfigDebugBeginReturnValueLoop;// = false          // Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add \"io.ConfigDebugBeginReturnValue = io.KeyShift\" in your main loop then occasionally press SHIFT. Windows should be flickering while running.\n\n    // Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data.\n    // Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them.\n    // Consider using e.g. Win32's IsDebuggerPresent() as an additional filter (or see ImOsIsDebuggerPresent() in imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version).\n    bool        ConfigDebugIgnoreFocusLoss;     // = false          // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys() in input processing.\n\n    // Options to audit .ini data\n    bool        ConfigDebugIniSettings;         // = false          // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)\n\n    //------------------------------------------------------------------\n    // Platform Functions\n    // (the imgui_impl_xxxx backend files are setting those up for you)\n    //------------------------------------------------------------------\n\n    // Optional: Platform/Renderer backend name (informational only! will be displayed in About Window) + User data for backend/wrappers to store their own stuff.\n    const char* BackendPlatformName;            // = NULL\n    const char* BackendRendererName;            // = NULL\n    void*       BackendPlatformUserData;        // = NULL           // User data for platform backend\n    void*       BackendRendererUserData;        // = NULL           // User data for renderer backend\n    void*       BackendLanguageUserData;        // = NULL           // User data for non C++ programming language backend\n\n    // Optional: Access OS clipboard\n    // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures)\n    const char* (*GetClipboardTextFn)(void* user_data);\n    void        (*SetClipboardTextFn)(void* user_data, const char* text);\n    void*       ClipboardUserData;\n\n    // Optional: Notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME on Windows)\n    // (default to use native imm32 api on Windows)\n    void        (*SetPlatformImeDataFn)(ImGuiViewport* viewport, ImGuiPlatformImeData* data);\n\n    // Optional: Platform locale\n    ImWchar     PlatformLocaleDecimalPoint;     // '.'              // [Experimental] Configure decimal point e.g. '.' or ',' useful for some languages (e.g. German), generally pulled from *localeconv()->decimal_point\n\n    //------------------------------------------------------------------\n    // Input - Call before calling NewFrame()\n    //------------------------------------------------------------------\n\n    // Input Functions\n    IMGUI_API void  AddKeyEvent(ImGuiKey key, bool down);                   // Queue a new key down/up event. Key should be \"translated\" (as in, generally ImGuiKey_A matches the key end-user would use to emit an 'A' character)\n    IMGUI_API void  AddKeyAnalogEvent(ImGuiKey key, bool down, float v);    // Queue a new key down/up event for analog values (e.g. ImGuiKey_Gamepad_ values). Dead-zones should be handled by the backend.\n    IMGUI_API void  AddMousePosEvent(float x, float y);                     // Queue a mouse position update. Use -FLT_MAX,-FLT_MAX to signify no mouse (e.g. app not focused and not hovered)\n    IMGUI_API void  AddMouseButtonEvent(int button, bool down);             // Queue a mouse button change\n    IMGUI_API void  AddMouseWheelEvent(float wheel_x, float wheel_y);       // Queue a mouse wheel update. wheel_y<0: scroll down, wheel_y>0: scroll up, wheel_x<0: scroll right, wheel_x>0: scroll left.\n    IMGUI_API void  AddMouseSourceEvent(ImGuiMouseSource source);           // Queue a mouse source change (Mouse/TouchScreen/Pen)\n    IMGUI_API void  AddFocusEvent(bool focused);                            // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window)\n    IMGUI_API void  AddInputCharacter(unsigned int c);                      // Queue a new character input\n    IMGUI_API void  AddInputCharacterUTF16(ImWchar16 c);                    // Queue a new character input from a UTF-16 character, it can be a surrogate\n    IMGUI_API void  AddInputCharactersUTF8(const char* str);                // Queue a new characters input from a UTF-8 string\n\n    IMGUI_API void  SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode.\n    IMGUI_API void  SetAppAcceptingEvents(bool accepting_events);           // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen.\n    IMGUI_API void  ClearEventsQueue();                                     // Clear all incoming events.\n    IMGUI_API void  ClearInputKeys();                                       // Clear current keyboard/mouse/gamepad state + current frame text input buffer. Equivalent to releasing all keys/buttons.\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    IMGUI_API void  ClearInputCharacters();                                 // [Obsolete] Clear the current frame text input buffer. Now included within ClearInputKeys().\n#endif\n\n    //------------------------------------------------------------------\n    // Output - Updated by NewFrame() or EndFrame()/Render()\n    // (when reading from the io.WantCaptureMouse, io.WantCaptureKeyboard flags to dispatch your inputs, it is\n    //  generally easier and more correct to use their state BEFORE calling NewFrame(). See FAQ for details!)\n    //------------------------------------------------------------------\n\n    bool        WantCaptureMouse;                   // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.).\n    bool        WantCaptureKeyboard;                // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.).\n    bool        WantTextInput;                      // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active).\n    bool        WantSetMousePos;                    // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled.\n    bool        WantSaveIniSettings;                // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!\n    bool        NavActive;                          // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.\n    bool        NavVisible;                         // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events).\n    float       Framerate;                          // Estimate of application framerate (rolling average over 60 frames, based on io.DeltaTime), in frame per second. Solely for convenience. Slow applications may not want to use a moving average or may want to reset underlying buffers occasionally.\n    int         MetricsRenderVertices;              // Vertices output during last call to Render()\n    int         MetricsRenderIndices;               // Indices output during last call to Render() = number of triangles * 3\n    int         MetricsRenderWindows;               // Number of visible windows\n    int         MetricsActiveWindows;               // Number of active windows\n    ImVec2      MouseDelta;                         // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta.\n\n    // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame.\n    // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent().\n    //   Old (<1.87):  ImGui::IsKeyPressed(ImGui::GetIO().KeyMap[ImGuiKey_Space]) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space)\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    int         KeyMap[ImGuiKey_COUNT];             // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your \"native\" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512.\n    bool        KeysDown[ImGuiKey_COUNT];           // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the \"native\" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow.\n    float       NavInputs[ImGuiNavInput_COUNT];     // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums.\n#endif\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    void*       ImeWindowHandle;                    // = NULL   // [Obsoleted in 1.87] Set ImGuiViewport::PlatformHandleRaw instead. Set this to your HWND to get automatic IME cursor positioning.\n#else\n    void*       _UnusedPadding;\n#endif\n\n    //------------------------------------------------------------------\n    // [Internal] Dear ImGui will maintain those fields. Forward compatibility not guaranteed!\n    //------------------------------------------------------------------\n\n    ImGuiContext* Ctx;                              // Parent UI context (needs to be set explicitly by parent).\n\n    // Main Input State\n    // (this block used to be written by backend, since 1.87 it is best to NOT write to those directly, call the AddXXX functions above instead)\n    // (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere)\n    ImVec2      MousePos;                           // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.)\n    bool        MouseDown[5];                       // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Other buttons allow us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API.\n    float       MouseWheel;                         // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. >0 scrolls Up, <0 scrolls Down. Hold SHIFT to turn vertical scroll into horizontal scroll.\n    float       MouseWheelH;                        // Mouse wheel Horizontal. >0 scrolls Left, <0 scrolls Right. Most users don't have a mouse with a horizontal wheel, may not be filled by all backends.\n    ImGuiMouseSource MouseSource;                   // Mouse actual input peripheral (Mouse/TouchScreen/Pen).\n    bool        KeyCtrl;                            // Keyboard modifier down: Control\n    bool        KeyShift;                           // Keyboard modifier down: Shift\n    bool        KeyAlt;                             // Keyboard modifier down: Alt\n    bool        KeySuper;                           // Keyboard modifier down: Cmd/Super/Windows\n\n    // Other state maintained from data above + IO function calls\n    ImGuiKeyChord KeyMods;                          // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. DOES NOT CONTAINS ImGuiMod_Shortcut which is pretranslated). Read-only, updated by NewFrame()\n    ImGuiKeyData  KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this.\n    bool        WantCaptureMouseUnlessPopupClose;   // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup.\n    ImVec2      MousePosPrev;                       // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid)\n    ImVec2      MouseClickedPos[5];                 // Position at time of clicking\n    double      MouseClickedTime[5];                // Time of last click (used to figure out double-click)\n    bool        MouseClicked[5];                    // Mouse button went from !Down to Down (same as MouseClickedCount[x] != 0)\n    bool        MouseDoubleClicked[5];              // Has mouse button been double-clicked? (same as MouseClickedCount[x] == 2)\n    ImU16       MouseClickedCount[5];               // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down\n    ImU16       MouseClickedLastCount[5];           // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done.\n    bool        MouseReleased[5];                   // Mouse button went from Down to !Down\n    bool        MouseDownOwned[5];                  // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds.\n    bool        MouseDownOwnedUnlessPopupClose[5];  // Track if button was clicked inside a dear imgui window.\n    bool        MouseWheelRequestAxisSwap;          // On a non-Mac system, holding SHIFT requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system.\n    float       MouseDownDuration[5];               // Duration the mouse button has been down (0.0f == just clicked)\n    float       MouseDownDurationPrev[5];           // Previous time the mouse button has been down\n    float       MouseDragMaxDistanceSqr[5];         // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds)\n    float       PenPressure;                        // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui.\n    bool        AppFocusLost;                       // Only modify via AddFocusEvent()\n    bool        AppAcceptingEvents;                 // Only modify via SetAppAcceptingEvents()\n    ImS8        BackendUsingLegacyKeyArrays;        // -1: unknown, 0: using AddKeyEvent(), 1: using legacy io.KeysDown[]\n    bool        BackendUsingLegacyNavInputArray;    // 0: using AddKeyAnalogEvent(), 1: writing to legacy io.NavInputs[] directly\n    ImWchar16   InputQueueSurrogate;                // For AddInputCharacterUTF16()\n    ImVector<ImWchar> InputQueueCharacters;         // Queue of _characters_ input (obtained by platform backend). Fill using AddInputCharacter() helper.\n\n    IMGUI_API   ImGuiIO();\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Misc data structures\n//-----------------------------------------------------------------------------\n\n// Shared state of InputText(), passed as an argument to your callback when a ImGuiInputTextFlags_Callback* flag is used.\n// The callback function should return 0 by default.\n// Callbacks (follow a flag name and see comments in ImGuiInputTextFlags_ declarations for more details)\n// - ImGuiInputTextFlags_CallbackEdit:        Callback on buffer edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active)\n// - ImGuiInputTextFlags_CallbackAlways:      Callback on each iteration\n// - ImGuiInputTextFlags_CallbackCompletion:  Callback on pressing TAB\n// - ImGuiInputTextFlags_CallbackHistory:     Callback on pressing Up/Down arrows\n// - ImGuiInputTextFlags_CallbackCharFilter:  Callback on character inputs to replace or discard them. Modify 'EventChar' to replace or discard, or return 1 in callback to discard.\n// - ImGuiInputTextFlags_CallbackResize:      Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow.\nstruct ImGuiInputTextCallbackData\n{\n    ImGuiContext*       Ctx;            // Parent UI context\n    ImGuiInputTextFlags EventFlag;      // One ImGuiInputTextFlags_Callback*    // Read-only\n    ImGuiInputTextFlags Flags;          // What user passed to InputText()      // Read-only\n    void*               UserData;       // What user passed to InputText()      // Read-only\n\n    // Arguments for the different callback events\n    // - To modify the text buffer in a callback, prefer using the InsertChars() / DeleteChars() function. InsertChars() will take care of calling the resize callback if necessary.\n    // - If you know your edits are not going to resize the underlying buffer allocation, you may modify the contents of 'Buf[]' directly. You need to update 'BufTextLen' accordingly (0 <= BufTextLen < BufSize) and set 'BufDirty'' to true so InputText can update its internal state.\n    ImWchar             EventChar;      // Character input                      // Read-write   // [CharFilter] Replace character with another one, or set to zero to drop. return 1 is equivalent to setting EventChar=0;\n    ImGuiKey            EventKey;       // Key pressed (Up/Down/TAB)            // Read-only    // [Completion,History]\n    char*               Buf;            // Text buffer                          // Read-write   // [Resize] Can replace pointer / [Completion,History,Always] Only write to pointed data, don't replace the actual pointer!\n    int                 BufTextLen;     // Text length (in bytes)               // Read-write   // [Resize,Completion,History,Always] Exclude zero-terminator storage. In C land: == strlen(some_text), in C++ land: string.length()\n    int                 BufSize;        // Buffer size (in bytes) = capacity+1  // Read-only    // [Resize,Completion,History,Always] Include zero-terminator storage. In C land == ARRAYSIZE(my_char_array), in C++ land: string.capacity()+1\n    bool                BufDirty;       // Set if you modify Buf/BufTextLen!    // Write        // [Completion,History,Always]\n    int                 CursorPos;      //                                      // Read-write   // [Completion,History,Always]\n    int                 SelectionStart; //                                      // Read-write   // [Completion,History,Always] == to SelectionEnd when no selection)\n    int                 SelectionEnd;   //                                      // Read-write   // [Completion,History,Always]\n\n    // Helper functions for text manipulation.\n    // Use those function to benefit from the CallbackResize behaviors. Calling those function reset the selection.\n    IMGUI_API ImGuiInputTextCallbackData();\n    IMGUI_API void      DeleteChars(int pos, int bytes_count);\n    IMGUI_API void      InsertChars(int pos, const char* text, const char* text_end = NULL);\n    void                SelectAll()             { SelectionStart = 0; SelectionEnd = BufTextLen; }\n    void                ClearSelection()        { SelectionStart = SelectionEnd = BufTextLen; }\n    bool                HasSelection() const    { return SelectionStart != SelectionEnd; }\n};\n\n// Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin().\n// NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough.\nstruct ImGuiSizeCallbackData\n{\n    void*   UserData;       // Read-only.   What user passed to SetNextWindowSizeConstraints(). Generally store an integer or float in here (need reinterpret_cast<>).\n    ImVec2  Pos;            // Read-only.   Window position, for reference.\n    ImVec2  CurrentSize;    // Read-only.   Current window size.\n    ImVec2  DesiredSize;    // Read-write.  Desired size, based on user's mouse position. Write to this field to restrain resizing.\n};\n\n// Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload()\nstruct ImGuiPayload\n{\n    // Members\n    void*           Data;               // Data (copied and owned by dear imgui)\n    int             DataSize;           // Data size\n\n    // [Internal]\n    ImGuiID         SourceId;           // Source item id\n    ImGuiID         SourceParentId;     // Source parent id (if available)\n    int             DataFrameCount;     // Data timestamp\n    char            DataType[32 + 1];   // Data type tag (short user-supplied string, 32 characters max)\n    bool            Preview;            // Set when AcceptDragDropPayload() was called and mouse has been hovering the target item (nb: handle overlapping drag targets)\n    bool            Delivery;           // Set when AcceptDragDropPayload() was called and mouse button is released over the target item.\n\n    ImGuiPayload()  { Clear(); }\n    void Clear()    { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; }\n    bool IsDataType(const char* type) const { return DataFrameCount != -1 && strcmp(type, DataType) == 0; }\n    bool IsPreview() const                  { return Preview; }\n    bool IsDelivery() const                 { return Delivery; }\n};\n\n// Sorting specification for one column of a table (sizeof == 12 bytes)\nstruct ImGuiTableColumnSortSpecs\n{\n    ImGuiID                     ColumnUserID;       // User id of the column (if specified by a TableSetupColumn() call)\n    ImS16                       ColumnIndex;        // Index of the column\n    ImS16                       SortOrder;          // Index within parent ImGuiTableSortSpecs (always stored in order starting from 0, tables sorted on a single criteria will always have a 0 here)\n    ImGuiSortDirection          SortDirection : 8;  // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending\n\n    ImGuiTableColumnSortSpecs() { memset(this, 0, sizeof(*this)); }\n};\n\n// Sorting specifications for a table (often handling sort specs for a single column, occasionally more)\n// Obtained by calling TableGetSortSpecs().\n// When 'SpecsDirty == true' you can sort your data. It will be true with sorting specs have changed since last call, or the first time.\n// Make sure to set 'SpecsDirty = false' after sorting, else you may wastefully sort your data every frame!\nstruct ImGuiTableSortSpecs\n{\n    const ImGuiTableColumnSortSpecs* Specs;     // Pointer to sort spec array.\n    int                         SpecsCount;     // Sort spec count. Most often 1. May be > 1 when ImGuiTableFlags_SortMulti is enabled. May be == 0 when ImGuiTableFlags_SortTristate is enabled.\n    bool                        SpecsDirty;     // Set to true when specs have changed since last time! Use this to sort again, then clear the flag.\n\n    ImGuiTableSortSpecs()       { memset(this, 0, sizeof(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor)\n//-----------------------------------------------------------------------------\n\n// Helper: Unicode defines\n#define IM_UNICODE_CODEPOINT_INVALID 0xFFFD     // Invalid Unicode code point (standard value).\n#ifdef IMGUI_USE_WCHAR32\n#define IM_UNICODE_CODEPOINT_MAX     0x10FFFF   // Maximum Unicode code point supported by this build.\n#else\n#define IM_UNICODE_CODEPOINT_MAX     0xFFFF     // Maximum Unicode code point supported by this build.\n#endif\n\n// Helper: Execute a block of code at maximum once a frame. Convenient if you want to quickly create a UI within deep-nested code that runs multiple times every frame.\n// Usage: static ImGuiOnceUponAFrame oaf; if (oaf) ImGui::Text(\"This will be called only once per frame\");\nstruct ImGuiOnceUponAFrame\n{\n    ImGuiOnceUponAFrame() { RefFrame = -1; }\n    mutable int RefFrame;\n    operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; }\n};\n\n// Helper: Parse and apply text filters. In format \"aaaaa[,bbbb][,ccccc]\"\nstruct ImGuiTextFilter\n{\n    IMGUI_API           ImGuiTextFilter(const char* default_filter = \"\");\n    IMGUI_API bool      Draw(const char* label = \"Filter (inc,-exc)\", float width = 0.0f);  // Helper calling InputText+Build\n    IMGUI_API bool      PassFilter(const char* text, const char* text_end = NULL) const;\n    IMGUI_API void      Build();\n    void                Clear()          { InputBuf[0] = 0; Build(); }\n    bool                IsActive() const { return !Filters.empty(); }\n\n    // [Internal]\n    struct ImGuiTextRange\n    {\n        const char*     b;\n        const char*     e;\n\n        ImGuiTextRange()                                { b = e = NULL; }\n        ImGuiTextRange(const char* _b, const char* _e)  { b = _b; e = _e; }\n        bool            empty() const                   { return b == e; }\n        IMGUI_API void  split(char separator, ImVector<ImGuiTextRange>* out) const;\n    };\n    char                    InputBuf[256];\n    ImVector<ImGuiTextRange>Filters;\n    int                     CountGrep;\n};\n\n// Helper: Growable text buffer for logging/accumulating text\n// (this could be called 'ImGuiTextBuilder' / 'ImGuiStringBuilder')\nstruct ImGuiTextBuffer\n{\n    ImVector<char>      Buf;\n    IMGUI_API static char EmptyString[1];\n\n    ImGuiTextBuffer()   { }\n    inline char         operator[](int i) const { IM_ASSERT(Buf.Data != NULL); return Buf.Data[i]; }\n    const char*         begin() const           { return Buf.Data ? &Buf.front() : EmptyString; }\n    const char*         end() const             { return Buf.Data ? &Buf.back() : EmptyString; }   // Buf is zero-terminated, so end() will point on the zero-terminator\n    int                 size() const            { return Buf.Size ? Buf.Size - 1 : 0; }\n    bool                empty() const           { return Buf.Size <= 1; }\n    void                clear()                 { Buf.clear(); }\n    void                reserve(int capacity)   { Buf.reserve(capacity); }\n    const char*         c_str() const           { return Buf.Data ? Buf.Data : EmptyString; }\n    IMGUI_API void      append(const char* str, const char* str_end = NULL);\n    IMGUI_API void      appendf(const char* fmt, ...) IM_FMTARGS(2);\n    IMGUI_API void      appendfv(const char* fmt, va_list args) IM_FMTLIST(2);\n};\n\n// Helper: Key->Value storage\n// Typically you don't have to worry about this since a storage is held within each Window.\n// We use it to e.g. store collapse state for a tree (Int 0/1)\n// This is optimized for efficient lookup (dichotomy into a contiguous buffer) and rare insertion (typically tied to user interactions aka max once a frame)\n// You can use it as custom user storage for temporary values. Declare your own storage if, for example:\n// - You want to manipulate the open/close state of a particular sub-tree in your interface (tree node uses Int 0/1 to store their state).\n// - You want to store custom debug data easily without adding or editing structures in your code (probably not efficient, but convenient)\n// Types are NOT stored, so it is up to you to make sure your Key don't collide with different types.\nstruct ImGuiStorage\n{\n    // [Internal]\n    struct ImGuiStoragePair\n    {\n        ImGuiID key;\n        union { int val_i; float val_f; void* val_p; };\n        ImGuiStoragePair(ImGuiID _key, int _val)    { key = _key; val_i = _val; }\n        ImGuiStoragePair(ImGuiID _key, float _val)  { key = _key; val_f = _val; }\n        ImGuiStoragePair(ImGuiID _key, void* _val)  { key = _key; val_p = _val; }\n    };\n\n    ImVector<ImGuiStoragePair>      Data;\n\n    // - Get***() functions find pair, never add/allocate. Pairs are sorted so a query is O(log N)\n    // - Set***() functions find pair, insertion on demand if missing.\n    // - Sorted insertion is costly, paid once. A typical frame shouldn't need to insert any new pair.\n    void                Clear() { Data.clear(); }\n    IMGUI_API int       GetInt(ImGuiID key, int default_val = 0) const;\n    IMGUI_API void      SetInt(ImGuiID key, int val);\n    IMGUI_API bool      GetBool(ImGuiID key, bool default_val = false) const;\n    IMGUI_API void      SetBool(ImGuiID key, bool val);\n    IMGUI_API float     GetFloat(ImGuiID key, float default_val = 0.0f) const;\n    IMGUI_API void      SetFloat(ImGuiID key, float val);\n    IMGUI_API void*     GetVoidPtr(ImGuiID key) const; // default_val is NULL\n    IMGUI_API void      SetVoidPtr(ImGuiID key, void* val);\n\n    // - Get***Ref() functions finds pair, insert on demand if missing, return pointer. Useful if you intend to do Get+Set.\n    // - References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer.\n    // - A typical use case where this is convenient for quick hacking (e.g. add storage during a live Edit&Continue session if you can't modify existing struct)\n    //      float* pvar = ImGui::GetFloatRef(key); ImGui::SliderFloat(\"var\", pvar, 0, 100.0f); some_var += *pvar;\n    IMGUI_API int*      GetIntRef(ImGuiID key, int default_val = 0);\n    IMGUI_API bool*     GetBoolRef(ImGuiID key, bool default_val = false);\n    IMGUI_API float*    GetFloatRef(ImGuiID key, float default_val = 0.0f);\n    IMGUI_API void**    GetVoidPtrRef(ImGuiID key, void* default_val = NULL);\n\n    // Advanced: for quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once.\n    IMGUI_API void      BuildSortByKey();\n    // Obsolete: use on your own storage if you know only integer are being stored (open/close all tree nodes)\n    IMGUI_API void      SetAllInt(int val);\n};\n\n// Helper: Manually clip large list of items.\n// If you have lots evenly spaced items and you have random access to the list, you can perform coarse\n// clipping based on visibility to only submit items that are in view.\n// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped.\n// (Dear ImGui already clip items based on their bounds but: it needs to first layout the item to do so, and generally\n//  fetching/submitting your own data incurs additional cost. Coarse clipping using ImGuiListClipper allows you to easily\n//  scale using lists with tens of thousands of items without a problem)\n// Usage:\n//   ImGuiListClipper clipper;\n//   clipper.Begin(1000);         // We have 1000 elements, evenly spaced.\n//   while (clipper.Step())\n//       for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)\n//           ImGui::Text(\"line number %d\", i);\n// Generally what happens is:\n// - Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not.\n// - User code submit that one element.\n// - Clipper can measure the height of the first element\n// - Clipper calculate the actual range of elements to display based on the current clipping rectangle, position the cursor before the first visible element.\n// - User code submit visible elements.\n// - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc.\nstruct ImGuiListClipper\n{\n    ImGuiContext*   Ctx;                // Parent UI context\n    int             DisplayStart;       // First item to display, updated by each call to Step()\n    int             DisplayEnd;         // End of items to display (exclusive)\n    int             ItemsCount;         // [Internal] Number of items\n    float           ItemsHeight;        // [Internal] Height of item after a first step and item submission can calculate it\n    float           StartPosY;          // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed\n    void*           TempData;           // [Internal] Internal data\n\n    // items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step)\n    // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing().\n    IMGUI_API ImGuiListClipper();\n    IMGUI_API ~ImGuiListClipper();\n    IMGUI_API void  Begin(int items_count, float items_height = -1.0f);\n    IMGUI_API void  End();             // Automatically called on the last call of Step() that returns false.\n    IMGUI_API bool  Step();            // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items.\n\n    // Call IncludeItemByIndex() or IncludeItemsByIndex() *BEFORE* first call to Step() if you need a range of items to not be clipped, regardless of their visibility.\n    // (Due to alignment / padding of certain items it is possible that an extra item may be included on either end of the display range).\n    inline void     IncludeItemByIndex(int item_index)                  { IncludeItemsByIndex(item_index, item_index + 1); }\n    IMGUI_API void  IncludeItemsByIndex(int item_begin, int item_end);  // item_end is exclusive e.g. use (42, 42+1) to make item 42 never clipped.\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    inline void IncludeRangeByIndices(int item_begin, int item_end)      { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9]\n    inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6]\n    //inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79]\n#endif\n};\n\n// Helpers: ImVec2/ImVec4 operators\n// - It is important that we are keeping those disabled by default so they don't leak in user space.\n// - This is in order to allow user enabling implicit cast operators between ImVec2/ImVec4 and their own types (using IM_VEC2_CLASS_EXTRA in imconfig.h)\n// - You can use '#define IMGUI_DEFINE_MATH_OPERATORS' to import our operators, provided as a courtesy.\n#ifdef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED\nIM_MSVC_RUNTIME_CHECKS_OFF\nstatic inline ImVec2  operator*(const ImVec2& lhs, const float rhs)     { return ImVec2(lhs.x * rhs, lhs.y * rhs); }\nstatic inline ImVec2  operator/(const ImVec2& lhs, const float rhs)     { return ImVec2(lhs.x / rhs, lhs.y / rhs); }\nstatic inline ImVec2  operator+(const ImVec2& lhs, const ImVec2& rhs)   { return ImVec2(lhs.x + rhs.x, lhs.y + rhs.y); }\nstatic inline ImVec2  operator-(const ImVec2& lhs, const ImVec2& rhs)   { return ImVec2(lhs.x - rhs.x, lhs.y - rhs.y); }\nstatic inline ImVec2  operator*(const ImVec2& lhs, const ImVec2& rhs)   { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }\nstatic inline ImVec2  operator/(const ImVec2& lhs, const ImVec2& rhs)   { return ImVec2(lhs.x / rhs.x, lhs.y / rhs.y); }\nstatic inline ImVec2  operator-(const ImVec2& lhs)                      { return ImVec2(-lhs.x, -lhs.y); }\nstatic inline ImVec2& operator*=(ImVec2& lhs, const float rhs)          { lhs.x *= rhs; lhs.y *= rhs; return lhs; }\nstatic inline ImVec2& operator/=(ImVec2& lhs, const float rhs)          { lhs.x /= rhs; lhs.y /= rhs; return lhs; }\nstatic inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs)        { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; }\nstatic inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs)        { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; }\nstatic inline ImVec2& operator*=(ImVec2& lhs, const ImVec2& rhs)        { lhs.x *= rhs.x; lhs.y *= rhs.y; return lhs; }\nstatic inline ImVec2& operator/=(ImVec2& lhs, const ImVec2& rhs)        { lhs.x /= rhs.x; lhs.y /= rhs.y; return lhs; }\nstatic inline ImVec4  operator+(const ImVec4& lhs, const ImVec4& rhs)   { return ImVec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); }\nstatic inline ImVec4  operator-(const ImVec4& lhs, const ImVec4& rhs)   { return ImVec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); }\nstatic inline ImVec4  operator*(const ImVec4& lhs, const ImVec4& rhs)   { return ImVec4(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z, lhs.w * rhs.w); }\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n#endif\n\n// Helpers macros to generate 32-bit encoded colors\n// User can declare their own format by #defining the 5 _SHIFT/_MASK macros in their imconfig file.\n#ifndef IM_COL32_R_SHIFT\n#ifdef IMGUI_USE_BGRA_PACKED_COLOR\n#define IM_COL32_R_SHIFT    16\n#define IM_COL32_G_SHIFT    8\n#define IM_COL32_B_SHIFT    0\n#define IM_COL32_A_SHIFT    24\n#define IM_COL32_A_MASK     0xFF000000\n#else\n#define IM_COL32_R_SHIFT    0\n#define IM_COL32_G_SHIFT    8\n#define IM_COL32_B_SHIFT    16\n#define IM_COL32_A_SHIFT    24\n#define IM_COL32_A_MASK     0xFF000000\n#endif\n#endif\n#define IM_COL32(R,G,B,A)    (((ImU32)(A)<<IM_COL32_A_SHIFT) | ((ImU32)(B)<<IM_COL32_B_SHIFT) | ((ImU32)(G)<<IM_COL32_G_SHIFT) | ((ImU32)(R)<<IM_COL32_R_SHIFT))\n#define IM_COL32_WHITE       IM_COL32(255,255,255,255)  // Opaque white = 0xFFFFFFFF\n#define IM_COL32_BLACK       IM_COL32(0,0,0,255)        // Opaque black\n#define IM_COL32_BLACK_TRANS IM_COL32(0,0,0,0)          // Transparent black = 0x00000000\n\n// Helper: ImColor() implicitly converts colors to either ImU32 (packed 4x1 byte) or ImVec4 (4x1 float)\n// Prefer using IM_COL32() macros if you want a guaranteed compile-time ImU32 for usage with ImDrawList API.\n// **Avoid storing ImColor! Store either u32 of ImVec4. This is not a full-featured color class. MAY OBSOLETE.\n// **None of the ImGui API are using ImColor directly but you can use it as a convenience to pass colors in either ImU32 or ImVec4 formats. Explicitly cast to ImU32 or ImVec4 if needed.\nstruct ImColor\n{\n    ImVec4          Value;\n\n    constexpr ImColor()                                             { }\n    constexpr ImColor(float r, float g, float b, float a = 1.0f)    : Value(r, g, b, a) { }\n    constexpr ImColor(const ImVec4& col)                            : Value(col) {}\n    constexpr ImColor(int r, int g, int b, int a = 255)             : Value((float)r * (1.0f / 255.0f), (float)g * (1.0f / 255.0f), (float)b * (1.0f / 255.0f), (float)a* (1.0f / 255.0f)) {}\n    constexpr ImColor(ImU32 rgba)                                   : Value((float)((rgba >> IM_COL32_R_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_G_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_B_SHIFT) & 0xFF) * (1.0f / 255.0f), (float)((rgba >> IM_COL32_A_SHIFT) & 0xFF) * (1.0f / 255.0f)) {}\n    inline operator ImU32() const                                   { return ImGui::ColorConvertFloat4ToU32(Value); }\n    inline operator ImVec4() const                                  { return Value; }\n\n    // FIXME-OBSOLETE: May need to obsolete/cleanup those helpers.\n    inline void    SetHSV(float h, float s, float v, float a = 1.0f){ ImGui::ColorConvertHSVtoRGB(h, s, v, Value.x, Value.y, Value.z); Value.w = a; }\n    static ImColor HSV(float h, float s, float v, float a = 1.0f)   { float r, g, b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r, g, b, a); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Drawing API (ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawListFlags, ImDrawList, ImDrawData)\n// Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList.\n//-----------------------------------------------------------------------------\n\n// The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoBakedLines to disable baking.\n#ifndef IM_DRAWLIST_TEX_LINES_WIDTH_MAX\n#define IM_DRAWLIST_TEX_LINES_WIDTH_MAX     (63)\n#endif\n\n// ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h]\n// NB: You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering,\n// you can poke into the draw list for that! Draw callback may be useful for example to:\n//  A) Change your GPU render state,\n//  B) render a complex 3D scene inside a UI element without an intermediate texture/render target, etc.\n// The expected behavior from your rendering function is 'if (cmd.UserCallback != NULL) { cmd.UserCallback(parent_list, cmd); } else { RenderTriangles() }'\n// If you want to override the signature of ImDrawCallback, you can simply use e.g. '#define ImDrawCallback MyDrawCallback' (in imconfig.h) + update rendering backend accordingly.\n#ifndef ImDrawCallback\ntypedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd);\n#endif\n\n// Special Draw callback value to request renderer backend to reset the graphics/render state.\n// The renderer backend needs to handle this special value, otherwise it will crash trying to call a function at this address.\n// This is useful, for example, if you submitted callbacks which you know have altered the render state and you want it to be restored.\n// Render state is not reset by default because they are many perfectly useful way of altering render state (e.g. changing shader/blending settings before an Image call).\n#define ImDrawCallback_ResetRenderState     (ImDrawCallback)(-8)\n\n// Typically, 1 command = 1 GPU draw call (unless command is a callback)\n// - VtxOffset: When 'io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset' is enabled,\n//   this fields allow us to render meshes larger than 64K vertices while keeping 16-bit indices.\n//   Backends made for <1.71. will typically ignore the VtxOffset fields.\n// - The ClipRect/TextureId/VtxOffset fields must be contiguous as we memcmp() them together (this is asserted for).\nstruct ImDrawCmd\n{\n    ImVec4          ClipRect;           // 4*4  // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in \"viewport\" coordinates\n    ImTextureID     TextureId;          // 4-8  // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas.\n    unsigned int    VtxOffset;          // 4    // Start offset in vertex buffer. ImGuiBackendFlags_RendererHasVtxOffset: always 0, otherwise may be >0 to support meshes larger than 64K vertices with 16-bit indices.\n    unsigned int    IdxOffset;          // 4    // Start offset in index buffer.\n    unsigned int    ElemCount;          // 4    // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[].\n    ImDrawCallback  UserCallback;       // 4-8  // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally.\n    void*           UserCallbackData;   // 4-8  // The draw callback code can access this.\n\n    ImDrawCmd() { memset(this, 0, sizeof(*this)); } // Also ensure our padding fields are zeroed\n\n    // Since 1.83: returns ImTextureID associated with this draw call. Warning: DO NOT assume this is always same as 'TextureId' (we will change this function for an upcoming feature)\n    inline ImTextureID GetTexID() const { return TextureId; }\n};\n\n// Vertex layout\n#ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT\nstruct ImDrawVert\n{\n    ImVec2  pos;\n    ImVec2  uv;\n    ImU32   col;\n};\n#else\n// You can override the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT in imconfig.h\n// The code expect ImVec2 pos (8 bytes), ImVec2 uv (8 bytes), ImU32 col (4 bytes), but you can re-order them or add other fields as needed to simplify integration in your engine.\n// The type has to be described within the macro (you can either declare the struct or use a typedef). This is because ImVec2/ImU32 are likely not declared at the time you'd want to set your type up.\n// NOTE: IMGUI DOESN'T CLEAR THE STRUCTURE AND DOESN'T CALL A CONSTRUCTOR SO ANY CUSTOM FIELD WILL BE UNINITIALIZED. IF YOU ADD EXTRA FIELDS (SUCH AS A 'Z' COORDINATES) YOU WILL NEED TO CLEAR THEM DURING RENDER OR TO IGNORE THEM.\nIMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT;\n#endif\n\n// [Internal] For use by ImDrawList\nstruct ImDrawCmdHeader\n{\n    ImVec4          ClipRect;\n    ImTextureID     TextureId;\n    unsigned int    VtxOffset;\n};\n\n// [Internal] For use by ImDrawListSplitter\nstruct ImDrawChannel\n{\n    ImVector<ImDrawCmd>         _CmdBuffer;\n    ImVector<ImDrawIdx>         _IdxBuffer;\n};\n\n\n// Split/Merge functions are used to split the draw list into different layers which can be drawn into out of order.\n// This is used by the Columns/Tables API, so items of each column can be batched together in a same draw call.\nstruct ImDrawListSplitter\n{\n    int                         _Current;    // Current channel number (0)\n    int                         _Count;      // Number of active channels (1+)\n    ImVector<ImDrawChannel>     _Channels;   // Draw channels (not resized down so _Count might be < Channels.Size)\n\n    inline ImDrawListSplitter()  { memset(this, 0, sizeof(*this)); }\n    inline ~ImDrawListSplitter() { ClearFreeMemory(); }\n    inline void                 Clear() { _Current = 0; _Count = 1; } // Do not clear Channels[] so our allocations are reused next frame\n    IMGUI_API void              ClearFreeMemory();\n    IMGUI_API void              Split(ImDrawList* draw_list, int count);\n    IMGUI_API void              Merge(ImDrawList* draw_list);\n    IMGUI_API void              SetCurrentChannel(ImDrawList* draw_list, int channel_idx);\n};\n\n// Flags for ImDrawList functions\n// (Legacy: bit 0 must always correspond to ImDrawFlags_Closed to be backward compatible with old API using a bool. Bits 1..3 must be unused)\nenum ImDrawFlags_\n{\n    ImDrawFlags_None                        = 0,\n    ImDrawFlags_Closed                      = 1 << 0, // PathStroke(), AddPolyline(): specify that shape should be closed (Important: this is always == 1 for legacy reason)\n    ImDrawFlags_RoundCornersTopLeft         = 1 << 4, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-left corner only (when rounding > 0.0f, we default to all corners). Was 0x01.\n    ImDrawFlags_RoundCornersTopRight        = 1 << 5, // AddRect(), AddRectFilled(), PathRect(): enable rounding top-right corner only (when rounding > 0.0f, we default to all corners). Was 0x02.\n    ImDrawFlags_RoundCornersBottomLeft      = 1 << 6, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-left corner only (when rounding > 0.0f, we default to all corners). Was 0x04.\n    ImDrawFlags_RoundCornersBottomRight     = 1 << 7, // AddRect(), AddRectFilled(), PathRect(): enable rounding bottom-right corner only (when rounding > 0.0f, we default to all corners). Wax 0x08.\n    ImDrawFlags_RoundCornersNone            = 1 << 8, // AddRect(), AddRectFilled(), PathRect(): disable rounding on all corners (when rounding > 0.0f). This is NOT zero, NOT an implicit flag!\n    ImDrawFlags_RoundCornersTop             = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight,\n    ImDrawFlags_RoundCornersBottom          = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,\n    ImDrawFlags_RoundCornersLeft            = ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersTopLeft,\n    ImDrawFlags_RoundCornersRight           = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight,\n    ImDrawFlags_RoundCornersAll             = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight,\n    ImDrawFlags_RoundCornersDefault_        = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified.\n    ImDrawFlags_RoundCornersMask_           = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone,\n};\n\n// Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly.\n// It is however possible to temporarily alter flags between calls to ImDrawList:: functions.\nenum ImDrawListFlags_\n{\n    ImDrawListFlags_None                    = 0,\n    ImDrawListFlags_AntiAliasedLines        = 1 << 0,  // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles)\n    ImDrawListFlags_AntiAliasedLinesUseTex  = 1 << 1,  // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering).\n    ImDrawListFlags_AntiAliasedFill         = 1 << 2,  // Enable anti-aliased edge around filled shapes (rounded rectangles, circles).\n    ImDrawListFlags_AllowVtxOffset          = 1 << 3,  // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled.\n};\n\n// Draw command list\n// This is the low-level list of polygons that ImGui:: functions are filling. At the end of the frame,\n// all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering.\n// Each dear imgui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to\n// access the current window draw list and draw custom primitives.\n// You can interleave normal ImGui:: calls and adding primitives to the current draw list.\n// In single viewport mode, top-left is == GetMainViewport()->Pos (generally 0,0), bottom-right is == GetMainViewport()->Pos+Size (generally io.DisplaySize).\n// You are totally free to apply whatever transformation matrix to want to the data (depending on the use of the transformation you may want to apply it to ClipRect as well!)\n// Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects.\nstruct ImDrawList\n{\n    // This is what you have to render\n    ImVector<ImDrawCmd>     CmdBuffer;          // Draw commands. Typically 1 command = 1 GPU draw call, unless the command is a callback.\n    ImVector<ImDrawIdx>     IdxBuffer;          // Index buffer. Each command consume ImDrawCmd::ElemCount of those\n    ImVector<ImDrawVert>    VtxBuffer;          // Vertex buffer.\n    ImDrawListFlags         Flags;              // Flags, you may poke into these to adjust anti-aliasing settings per-primitive.\n\n    // [Internal, used while building lists]\n    unsigned int            _VtxCurrentIdx;     // [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0.\n    ImDrawListSharedData*   _Data;              // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context)\n    const char*             _OwnerName;         // Pointer to owner window's name for debugging\n    ImDrawVert*             _VtxWritePtr;       // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)\n    ImDrawIdx*              _IdxWritePtr;       // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much)\n    ImVector<ImVec4>        _ClipRectStack;     // [Internal]\n    ImVector<ImTextureID>   _TextureIdStack;    // [Internal]\n    ImVector<ImVec2>        _Path;              // [Internal] current path building\n    ImDrawCmdHeader         _CmdHeader;         // [Internal] template of active commands. Fields should match those of CmdBuffer.back().\n    ImDrawListSplitter      _Splitter;          // [Internal] for channels api (note: prefer using your own persistent instance of ImDrawListSplitter!)\n    float                   _FringeScale;       // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content\n\n    // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData() or create and use your own ImDrawListSharedData (so you can use ImDrawList without ImGui)\n    ImDrawList(ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; }\n\n    ~ImDrawList() { _ClearFreeMemory(); }\n    IMGUI_API void  PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false);  // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)\n    IMGUI_API void  PushClipRectFullScreen();\n    IMGUI_API void  PopClipRect();\n    IMGUI_API void  PushTextureID(ImTextureID texture_id);\n    IMGUI_API void  PopTextureID();\n    inline ImVec2   GetClipRectMin() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.x, cr.y); }\n    inline ImVec2   GetClipRectMax() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.z, cr.w); }\n\n    // Primitives\n    // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have \"inward\" anti-aliasing.\n    // - For rectangular primitives, \"p_min\" and \"p_max\" represent the upper-left and lower-right corners.\n    // - For circle primitives, use \"num_segments == 0\" to automatically calculate tessellation (preferred).\n    //   In older versions (until Dear ImGui 1.77) the AddCircle functions defaulted to num_segments == 12.\n    //   In future versions we will use textures to provide cheaper and higher-quality circles.\n    //   Use AddNgon() and AddNgonFilled() functions if you need to guarantee a specific number of sides.\n    IMGUI_API void  AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f);\n    IMGUI_API void  AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f);   // a: upper-left, b: lower-right (== upper-left + size)\n    IMGUI_API void  AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0);                     // a: upper-left, b: lower-right (== upper-left + size)\n    IMGUI_API void  AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left);\n    IMGUI_API void  AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness = 1.0f);\n    IMGUI_API void  AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col);\n    IMGUI_API void  AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness = 1.0f);\n    IMGUI_API void  AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col);\n    IMGUI_API void  AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments = 0, float thickness = 1.0f);\n    IMGUI_API void  AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments = 0);\n    IMGUI_API void  AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness = 1.0f);\n    IMGUI_API void  AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments);\n    IMGUI_API void  AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0, float thickness = 1.0f);\n    IMGUI_API void  AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot = 0.0f, int num_segments = 0);\n    IMGUI_API void  AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL);\n    IMGUI_API void  AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL);\n    IMGUI_API void  AddPolyline(const ImVec2* points, int num_points, ImU32 col, ImDrawFlags flags, float thickness);\n    IMGUI_API void  AddConvexPolyFilled(const ImVec2* points, int num_points, ImU32 col);\n    IMGUI_API void  AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0); // Cubic Bezier (4 control points)\n    IMGUI_API void  AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments = 0);               // Quadratic Bezier (3 control points)\n\n    // Image primitives\n    // - Read FAQ to understand what ImTextureID is.\n    // - \"p_min\" and \"p_max\" represent the upper-left and lower-right corners of the rectangle.\n    // - \"uv_min\" and \"uv_max\" represent the normalized texture coordinates to use for those corners. Using (0,0)->(1,1) texture coordinates will generally display the entire texture.\n    IMGUI_API void  AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min = ImVec2(0, 0), const ImVec2& uv_max = ImVec2(1, 1), ImU32 col = IM_COL32_WHITE);\n    IMGUI_API void  AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1 = ImVec2(0, 0), const ImVec2& uv2 = ImVec2(1, 0), const ImVec2& uv3 = ImVec2(1, 1), const ImVec2& uv4 = ImVec2(0, 1), ImU32 col = IM_COL32_WHITE);\n    IMGUI_API void  AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags = 0);\n\n    // Stateful path API, add points then finish with PathFillConvex() or PathStroke()\n    // - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have \"inward\" anti-aliasing.\n    inline    void  PathClear()                                                 { _Path.Size = 0; }\n    inline    void  PathLineTo(const ImVec2& pos)                               { _Path.push_back(pos); }\n    inline    void  PathLineToMergeDuplicate(const ImVec2& pos)                 { if (_Path.Size == 0 || memcmp(&_Path.Data[_Path.Size - 1], &pos, 8) != 0) _Path.push_back(pos); }\n    inline    void  PathFillConvex(ImU32 col)                                   { AddConvexPolyFilled(_Path.Data, _Path.Size, col); _Path.Size = 0; }\n    inline    void  PathStroke(ImU32 col, ImDrawFlags flags = 0, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, flags, thickness); _Path.Size = 0; }\n    IMGUI_API void  PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments = 0);\n    IMGUI_API void  PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12);                // Use precomputed angles for a 12 steps circle\n    IMGUI_API void  PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments = 0); // Ellipse\n    IMGUI_API void  PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0); // Cubic Bezier (4 control points)\n    IMGUI_API void  PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments = 0);               // Quadratic Bezier (3 control points)\n    IMGUI_API void  PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, ImDrawFlags flags = 0);\n\n    // Advanced\n    IMGUI_API void  AddCallback(ImDrawCallback callback, void* callback_data);  // Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles.\n    IMGUI_API void  AddDrawCmd();                                               // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible\n    IMGUI_API ImDrawList* CloneOutput() const;                                  // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer.\n\n    // Advanced: Channels\n    // - Use to split render into layers. By switching channels to can render out-of-order (e.g. submit FG primitives before BG primitives)\n    // - Use to minimize draw calls (e.g. if going back-and-forth between multiple clipping rectangles, prefer to append into separate channels then merge at the end)\n    // - This API shouldn't have been in ImDrawList in the first place!\n    //   Prefer using your own persistent instance of ImDrawListSplitter as you can stack them.\n    //   Using the ImDrawList::ChannelsXXXX you cannot stack a split over another.\n    inline void     ChannelsSplit(int count)    { _Splitter.Split(this, count); }\n    inline void     ChannelsMerge()             { _Splitter.Merge(this); }\n    inline void     ChannelsSetCurrent(int n)   { _Splitter.SetCurrentChannel(this, n); }\n\n    // Advanced: Primitives allocations\n    // - We render triangles (three vertices)\n    // - All primitives needs to be reserved via PrimReserve() beforehand.\n    IMGUI_API void  PrimReserve(int idx_count, int vtx_count);\n    IMGUI_API void  PrimUnreserve(int idx_count, int vtx_count);\n    IMGUI_API void  PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col);      // Axis aligned rectangle (composed of two triangles)\n    IMGUI_API void  PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col);\n    IMGUI_API void  PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col);\n    inline    void  PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col)    { _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; }\n    inline    void  PrimWriteIdx(ImDrawIdx idx)                                     { *_IdxWritePtr = idx; _IdxWritePtr++; }\n    inline    void  PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col)         { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } // Write vertex with unique index\n\n    // Obsolete names\n    //inline  void  AddBezierCurve(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments = 0) { AddBezierCubic(p1, p2, p3, p4, col, thickness, num_segments); } // OBSOLETED in 1.80 (Jan 2021)\n    //inline  void  PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments = 0) { PathBezierCubicCurveTo(p2, p3, p4, num_segments); } // OBSOLETED in 1.80 (Jan 2021)\n\n    // [Internal helpers]\n    IMGUI_API void  _ResetForNewFrame();\n    IMGUI_API void  _ClearFreeMemory();\n    IMGUI_API void  _PopUnusedDrawCmd();\n    IMGUI_API void  _TryMergeDrawCmds();\n    IMGUI_API void  _OnChangedClipRect();\n    IMGUI_API void  _OnChangedTextureID();\n    IMGUI_API void  _OnChangedVtxOffset();\n    IMGUI_API int   _CalcCircleAutoSegmentCount(float radius) const;\n    IMGUI_API void  _PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step);\n    IMGUI_API void  _PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments);\n};\n\n// All draw data to render a Dear ImGui frame\n// (NB: the style and the naming convention here is a little inconsistent, we currently preserve them for backward compatibility purpose,\n// as this is one of the oldest structure exposed by the library! Basically, ImDrawList == CmdList)\nstruct ImDrawData\n{\n    bool                Valid;              // Only valid after Render() is called and before the next NewFrame() is called.\n    int                 CmdListsCount;      // Number of ImDrawList* to render (should always be == CmdLists.size)\n    int                 TotalIdxCount;      // For convenience, sum of all ImDrawList's IdxBuffer.Size\n    int                 TotalVtxCount;      // For convenience, sum of all ImDrawList's VtxBuffer.Size\n    ImVector<ImDrawList*> CmdLists;         // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here.\n    ImVec2              DisplayPos;         // Top-left position of the viewport to render (== top-left of the orthogonal projection matrix to use) (== GetMainViewport()->Pos for the main viewport, == (0.0) in most single-viewport applications)\n    ImVec2              DisplaySize;        // Size of the viewport to render (== GetMainViewport()->Size for the main viewport, == io.DisplaySize in most single-viewport applications)\n    ImVec2              FramebufferScale;   // Amount of pixels for each unit of DisplaySize. Based on io.DisplayFramebufferScale. Generally (1,1) on normal display, (2,2) on OSX with Retina display.\n    ImGuiViewport*      OwnerViewport;      // Viewport carrying the ImDrawData instance, might be of use to the renderer (generally not).\n\n    // Functions\n    ImDrawData()    { Clear(); }\n    IMGUI_API void  Clear();\n    IMGUI_API void  AddDrawList(ImDrawList* draw_list);     // Helper to add an external draw list into an existing ImDrawData.\n    IMGUI_API void  DeIndexAllBuffers();                    // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!\n    IMGUI_API void  ScaleClipRects(const ImVec2& fb_scale); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than Dear ImGui expects, or if there is a difference between your window resolution and framebuffer resolution.\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont)\n//-----------------------------------------------------------------------------\n\nstruct ImFontConfig\n{\n    void*           FontData;               //          // TTF/OTF data\n    int             FontDataSize;           //          // TTF/OTF data size\n    bool            FontDataOwnedByAtlas;   // true     // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself).\n    int             FontNo;                 // 0        // Index of font within TTF/OTF file\n    float           SizePixels;             //          // Size in pixels for rasterizer (more or less maps to the resulting font height).\n    int             OversampleH;            // 2        // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details.\n    int             OversampleV;            // 1        // Rasterize at higher quality for sub-pixel positioning. This is not really useful as we don't use sub-pixel positions on the Y axis.\n    bool            PixelSnapH;             // false    // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1.\n    ImVec2          GlyphExtraSpacing;      // 0, 0     // Extra spacing (in pixels) between glyphs. Only X axis is supported for now.\n    ImVec2          GlyphOffset;            // 0, 0     // Offset all glyphs from this font input.\n    const ImWchar*  GlyphRanges;            // NULL     // THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list).\n    float           GlyphMinAdvanceX;       // 0        // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font\n    float           GlyphMaxAdvanceX;       // FLT_MAX  // Maximum AdvanceX for glyphs\n    bool            MergeMode;              // false    // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights.\n    unsigned int    FontBuilderFlags;       // 0        // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure.\n    float           RasterizerMultiply;     // 1.0f     // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future.\n    float           RasterizerDensity;      // 1.0f     // DPI scale for rasterization, not altering other font metrics: make it easy to swap between e.g. a 100% and a 400% fonts for a zooming display. IMPORTANT: If you increase this it is expected that you increase font scale accordingly, otherwise quality may look lowered.\n    ImWchar         EllipsisChar;           // -1       // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.\n\n    // [Internal]\n    char            Name[40];               // Name (strictly to ease debugging)\n    ImFont*         DstFont;\n\n    IMGUI_API ImFontConfig();\n};\n\n// Hold rendering data for one glyph.\n// (Note: some language parsers may fail to convert the 31+1 bitfield members, in this case maybe drop store a single u32 or we can rework this)\nstruct ImFontGlyph\n{\n    unsigned int    Colored : 1;        // Flag to indicate glyph is colored and should generally ignore tinting (make it usable with no shift on little-endian as this is used in loops)\n    unsigned int    Visible : 1;        // Flag to indicate glyph has no visible pixels (e.g. space). Allow early out when rendering.\n    unsigned int    Codepoint : 30;     // 0x0000..0x10FFFF\n    float           AdvanceX;           // Distance to next character (= data from font + ImFontConfig::GlyphExtraSpacing.x baked in)\n    float           X0, Y0, X1, Y1;     // Glyph corners\n    float           U0, V0, U1, V1;     // Texture coordinates\n};\n\n// Helper to build glyph ranges from text/string data. Feed your application strings/characters to it then call BuildRanges().\n// This is essentially a tightly packed of vector of 64k booleans = 8KB storage.\nstruct ImFontGlyphRangesBuilder\n{\n    ImVector<ImU32> UsedChars;            // Store 1-bit per Unicode code point (0=unused, 1=used)\n\n    ImFontGlyphRangesBuilder()              { Clear(); }\n    inline void     Clear()                 { int size_in_bytes = (IM_UNICODE_CODEPOINT_MAX + 1) / 8; UsedChars.resize(size_in_bytes / (int)sizeof(ImU32)); memset(UsedChars.Data, 0, (size_t)size_in_bytes); }\n    inline bool     GetBit(size_t n) const  { int off = (int)(n >> 5); ImU32 mask = 1u << (n & 31); return (UsedChars[off] & mask) != 0; }  // Get bit n in the array\n    inline void     SetBit(size_t n)        { int off = (int)(n >> 5); ImU32 mask = 1u << (n & 31); UsedChars[off] |= mask; }               // Set bit n in the array\n    inline void     AddChar(ImWchar c)      { SetBit(c); }                      // Add character\n    IMGUI_API void  AddText(const char* text, const char* text_end = NULL);     // Add string (each character of the UTF-8 string are added)\n    IMGUI_API void  AddRanges(const ImWchar* ranges);                           // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault()) to force add all of ASCII/Latin+Ext\n    IMGUI_API void  BuildRanges(ImVector<ImWchar>* out_ranges);                 // Output new ranges\n};\n\n// See ImFontAtlas::AddCustomRectXXX functions.\nstruct ImFontAtlasCustomRect\n{\n    unsigned short  Width, Height;  // Input    // Desired rectangle dimension\n    unsigned short  X, Y;           // Output   // Packed position in Atlas\n    unsigned int    GlyphID;        // Input    // For custom font glyphs only (ID < 0x110000)\n    float           GlyphAdvanceX;  // Input    // For custom font glyphs only: glyph xadvance\n    ImVec2          GlyphOffset;    // Input    // For custom font glyphs only: glyph display offset\n    ImFont*         Font;           // Input    // For custom font glyphs only: target font\n    ImFontAtlasCustomRect()         { Width = Height = 0; X = Y = 0xFFFF; GlyphID = 0; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0, 0); Font = NULL; }\n    bool IsPacked() const           { return X != 0xFFFF; }\n};\n\n// Flags for ImFontAtlas build\nenum ImFontAtlasFlags_\n{\n    ImFontAtlasFlags_None               = 0,\n    ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0,   // Don't round the height to next power of two\n    ImFontAtlasFlags_NoMouseCursors     = 1 << 1,   // Don't build software mouse cursors into the atlas (save a little texture memory)\n    ImFontAtlasFlags_NoBakedLines       = 1 << 2,   // Don't build thick line textures into the atlas (save a little texture memory, allow support for point/nearest filtering). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU).\n};\n\n// Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding:\n//  - One or more fonts.\n//  - Custom graphics data needed to render the shapes needed by Dear ImGui.\n//  - Mouse cursor shapes for software cursor rendering (unless setting 'Flags |= ImFontAtlasFlags_NoMouseCursors' in the font atlas).\n// It is the user-code responsibility to setup/build the atlas, then upload the pixel data into a texture accessible by your graphics api.\n//  - Optionally, call any of the AddFont*** functions. If you don't call any, the default font embedded in the code will be loaded for you.\n//  - Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data.\n//  - Upload the pixels data into a texture within your graphics system (see imgui_impl_xxxx.cpp examples)\n//  - Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture in a format natural to your graphics API.\n//    This value will be passed back to you during rendering to identify the texture. Read FAQ entry about ImTextureID for more details.\n// Common pitfalls:\n// - If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the\n//   atlas is build (when calling GetTexData*** or Build()). We only copy the pointer, not the data.\n// - Important: By default, AddFontFromMemoryTTF() takes ownership of the data. Even though we are not writing to it, we will free the pointer on destruction.\n//   You can set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed,\n// - Even though many functions are suffixed with \"TTF\", OTF data is supported just as well.\n// - This is an old API and it is currently awkward for those and various other reasons! We will address them in the future!\nstruct ImFontAtlas\n{\n    IMGUI_API ImFontAtlas();\n    IMGUI_API ~ImFontAtlas();\n    IMGUI_API ImFont*           AddFont(const ImFontConfig* font_cfg);\n    IMGUI_API ImFont*           AddFontDefault(const ImFontConfig* font_cfg = NULL);\n    IMGUI_API ImFont*           AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL);\n    IMGUI_API ImFont*           AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after destruction of the atlas. Set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed.\n    IMGUI_API ImFont*           AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp.\n    IMGUI_API ImFont*           AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL);              // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter.\n    IMGUI_API void              ClearInputData();           // Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts.\n    IMGUI_API void              ClearTexData();             // Clear output texture data (CPU side). Saves RAM once the texture has been copied to graphics memory.\n    IMGUI_API void              ClearFonts();               // Clear output font data (glyphs storage, UV coordinates).\n    IMGUI_API void              Clear();                    // Clear all input and output.\n\n    // Build atlas, retrieve pixel data.\n    // User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID().\n    // The pitch is always = Width * BytesPerPixels (1 or 4)\n    // Building in RGBA32 format is provided for convenience and compatibility, but note that unless you manually manipulate or copy color data into\n    // the texture (e.g. when using the AddCustomRect*** api), then the RGB pixels emitted will always be white (~75% of memory/bandwidth waste.\n    IMGUI_API bool              Build();                    // Build pixels data. This is called automatically for you by the GetTexData*** functions.\n    IMGUI_API void              GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL);  // 1 byte per-pixel\n    IMGUI_API void              GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL);  // 4 bytes-per-pixel\n    bool                        IsBuilt() const             { return Fonts.Size > 0 && TexReady; } // Bit ambiguous: used to detect when user didn't build texture but effectively we should check TexID != 0 except that would be backend dependent...\n    void                        SetTexID(ImTextureID id)    { TexID = id; }\n\n    //-------------------------------------------\n    // Glyph Ranges\n    //-------------------------------------------\n\n    // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list)\n    // NB: Make sure that your string are UTF-8 and NOT in your local code page.\n    // Read https://github.com/ocornut/imgui/blob/master/docs/FONTS.md/#about-utf-8-encoding for details.\n    // NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data.\n    IMGUI_API const ImWchar*    GetGlyphRangesDefault();                // Basic Latin, Extended Latin\n    IMGUI_API const ImWchar*    GetGlyphRangesGreek();                  // Default + Greek and Coptic\n    IMGUI_API const ImWchar*    GetGlyphRangesKorean();                 // Default + Korean characters\n    IMGUI_API const ImWchar*    GetGlyphRangesJapanese();               // Default + Hiragana, Katakana, Half-Width, Selection of 2999 Ideographs\n    IMGUI_API const ImWchar*    GetGlyphRangesChineseFull();            // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs\n    IMGUI_API const ImWchar*    GetGlyphRangesChineseSimplifiedCommon();// Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese\n    IMGUI_API const ImWchar*    GetGlyphRangesCyrillic();               // Default + about 400 Cyrillic characters\n    IMGUI_API const ImWchar*    GetGlyphRangesThai();                   // Default + Thai characters\n    IMGUI_API const ImWchar*    GetGlyphRangesVietnamese();             // Default + Vietnamese characters\n\n    //-------------------------------------------\n    // [BETA] Custom Rectangles/Glyphs API\n    //-------------------------------------------\n\n    // You can request arbitrary rectangles to be packed into the atlas, for your own purposes.\n    // - After calling Build(), you can query the rectangle position and render your pixels.\n    // - If you render colored output, set 'atlas->TexPixelsUseColors = true' as this may help some backends decide of prefered texture format.\n    // - You can also request your rectangles to be mapped as font glyph (given a font + Unicode point),\n    //   so you can render e.g. custom colorful icons and use them as regular glyphs.\n    // - Read docs/FONTS.md for more details about using colorful icons.\n    // - Note: this API may be redesigned later in order to support multi-monitor varying DPI settings.\n    IMGUI_API int               AddCustomRectRegular(int width, int height);\n    IMGUI_API int               AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0, 0));\n    ImFontAtlasCustomRect*      GetCustomRectByIndex(int index) { IM_ASSERT(index >= 0); return &CustomRects[index]; }\n\n    // [Internal]\n    IMGUI_API void              CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const;\n    IMGUI_API bool              GetMouseCursorTexData(ImGuiMouseCursor cursor, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]);\n\n    //-------------------------------------------\n    // Members\n    //-------------------------------------------\n\n    ImFontAtlasFlags            Flags;              // Build flags (see ImFontAtlasFlags_)\n    ImTextureID                 TexID;              // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure.\n    int                         TexDesiredWidth;    // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height.\n    int                         TexGlyphPadding;    // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false).\n    bool                        Locked;             // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert.\n    void*                       UserData;           // Store your own atlas related user-data (if e.g. you have multiple font atlas).\n\n    // [Internal]\n    // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you.\n    bool                        TexReady;           // Set when texture was built matching current font input\n    bool                        TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format.\n    unsigned char*              TexPixelsAlpha8;    // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight\n    unsigned int*               TexPixelsRGBA32;    // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4\n    int                         TexWidth;           // Texture width calculated during Build().\n    int                         TexHeight;          // Texture height calculated during Build().\n    ImVec2                      TexUvScale;         // = (1.0f/TexWidth, 1.0f/TexHeight)\n    ImVec2                      TexUvWhitePixel;    // Texture coordinates to a white pixel\n    ImVector<ImFont*>           Fonts;              // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font.\n    ImVector<ImFontAtlasCustomRect> CustomRects;    // Rectangles for packing custom texture data into the atlas.\n    ImVector<ImFontConfig>      ConfigData;         // Configuration data\n    ImVec4                      TexUvLines[IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1];  // UVs for baked anti-aliased lines\n\n    // [Internal] Font builder\n    const ImFontBuilderIO*      FontBuilderIO;      // Opaque interface to a font builder (default to stb_truetype, can be changed to use FreeType by defining IMGUI_ENABLE_FREETYPE).\n    unsigned int                FontBuilderFlags;   // Shared flags (for all fonts) for custom font builder. THIS IS BUILD IMPLEMENTATION DEPENDENT. Per-font override is also available in ImFontConfig.\n\n    // [Internal] Packing data\n    int                         PackIdMouseCursors; // Custom texture rectangle ID for white pixel and mouse cursors\n    int                         PackIdLines;        // Custom texture rectangle ID for baked anti-aliased lines\n\n    // [Obsolete]\n    //typedef ImFontAtlasCustomRect    CustomRect;         // OBSOLETED in 1.72+\n    //typedef ImFontGlyphRangesBuilder GlyphRangesBuilder; // OBSOLETED in 1.67+\n};\n\n// Font runtime data and rendering\n// ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32().\nstruct ImFont\n{\n    // Members: Hot ~20/24 bytes (for CalcTextSize)\n    ImVector<float>             IndexAdvanceX;      // 12-16 // out //            // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this this info, and are often bottleneck in large UI).\n    float                       FallbackAdvanceX;   // 4     // out // = FallbackGlyph->AdvanceX\n    float                       FontSize;           // 4     // in  //            // Height of characters/line, set during loading (don't change after loading)\n\n    // Members: Hot ~28/40 bytes (for CalcTextSize + render loop)\n    ImVector<ImWchar>           IndexLookup;        // 12-16 // out //            // Sparse. Index glyphs by Unicode code-point.\n    ImVector<ImFontGlyph>       Glyphs;             // 12-16 // out //            // All glyphs.\n    const ImFontGlyph*          FallbackGlyph;      // 4-8   // out // = FindGlyph(FontFallbackChar)\n\n    // Members: Cold ~32/40 bytes\n    ImFontAtlas*                ContainerAtlas;     // 4-8   // out //            // What we has been loaded into\n    const ImFontConfig*         ConfigData;         // 4-8   // in  //            // Pointer within ContainerAtlas->ConfigData\n    short                       ConfigDataCount;    // 2     // in  // ~ 1        // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont.\n    ImWchar                     FallbackChar;       // 2     // out // = FFFD/'?' // Character used if a glyph isn't found.\n    ImWchar                     EllipsisChar;       // 2     // out // = '...'/'.'// Character used for ellipsis rendering.\n    short                       EllipsisCharCount;  // 1     // out // 1 or 3\n    float                       EllipsisWidth;      // 4     // out               // Width\n    float                       EllipsisCharStep;   // 4     // out               // Step between characters when EllipsisCount > 0\n    bool                        DirtyLookupTables;  // 1     // out //\n    float                       Scale;              // 4     // in  // = 1.f      // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale()\n    float                       Ascent, Descent;    // 4+4   // out //            // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize]\n    int                         MetricsTotalSurface;// 4     // out //            // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs)\n    ImU8                        Used4kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/4096/8]; // 2 bytes if ImWchar=ImWchar16, 34 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints.\n\n    // Methods\n    IMGUI_API ImFont();\n    IMGUI_API ~ImFont();\n    IMGUI_API const ImFontGlyph*FindGlyph(ImWchar c) const;\n    IMGUI_API const ImFontGlyph*FindGlyphNoFallback(ImWchar c) const;\n    float                       GetCharAdvance(ImWchar c) const     { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; }\n    bool                        IsLoaded() const                    { return ContainerAtlas != NULL; }\n    const char*                 GetDebugName() const                { return ConfigData ? ConfigData->Name : \"<unknown>\"; }\n\n    // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable.\n    // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable.\n    IMGUI_API ImVec2            CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8\n    IMGUI_API const char*       CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const;\n    IMGUI_API void              RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c) const;\n    IMGUI_API void              RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const;\n\n    // [Internal] Don't use!\n    IMGUI_API void              BuildLookupTable();\n    IMGUI_API void              ClearOutputData();\n    IMGUI_API void              GrowIndex(int new_size);\n    IMGUI_API void              AddGlyph(const ImFontConfig* src_cfg, ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x);\n    IMGUI_API void              AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built.\n    IMGUI_API void              SetGlyphVisible(ImWchar c, bool visible);\n    IMGUI_API bool              IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last);\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Viewports\n//-----------------------------------------------------------------------------\n\n// Flags stored in ImGuiViewport::Flags, giving indications to the platform backends.\nenum ImGuiViewportFlags_\n{\n    ImGuiViewportFlags_None                     = 0,\n    ImGuiViewportFlags_IsPlatformWindow         = 1 << 0,   // Represent a Platform Window\n    ImGuiViewportFlags_IsPlatformMonitor        = 1 << 1,   // Represent a Platform Monitor (unused yet)\n    ImGuiViewportFlags_OwnedByApp               = 1 << 2,   // Platform Window: is created/managed by the application (rather than a dear imgui backend)\n};\n\n// - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows.\n// - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports.\n// - In the future we will extend this concept further to also represent Platform Monitor and support a \"no main platform window\" operation mode.\n// - About Main Area vs Work Area:\n//   - Main Area = entire viewport.\n//   - Work Area = entire viewport minus sections used by main menu bars (for platform windows), or by task bar (for platform monitor).\n//   - Windows are generally trying to stay within the Work Area of their host viewport.\nstruct ImGuiViewport\n{\n    ImGuiViewportFlags  Flags;                  // See ImGuiViewportFlags_\n    ImVec2              Pos;                    // Main Area: Position of the viewport (Dear ImGui coordinates are the same as OS desktop/native coordinates)\n    ImVec2              Size;                   // Main Area: Size of the viewport.\n    ImVec2              WorkPos;                // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos)\n    ImVec2              WorkSize;               // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size)\n\n    // Platform/Backend Dependent Data\n    void*               PlatformHandleRaw;      // void* to hold lower-level, platform-native window handle (under Win32 this is expected to be a HWND, unused for other platforms)\n\n    ImGuiViewport()     { memset(this, 0, sizeof(*this)); }\n\n    // Helpers\n    ImVec2              GetCenter() const       { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); }\n    ImVec2              GetWorkCenter() const   { return ImVec2(WorkPos.x + WorkSize.x * 0.5f, WorkPos.y + WorkSize.y * 0.5f); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Platform Dependent Interfaces\n//-----------------------------------------------------------------------------\n\n// (Optional) Support for IME (Input Method Editor) via the io.SetPlatformImeDataFn() function.\nstruct ImGuiPlatformImeData\n{\n    bool    WantVisible;        // A widget wants the IME to be visible\n    ImVec2  InputPos;           // Position of the input cursor\n    float   InputLineHeight;    // Line height\n\n    ImGuiPlatformImeData() { memset(this, 0, sizeof(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Obsolete functions and types\n// (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details)\n// Please keep your copy of dear imgui up to date! Occasionally set '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' in imconfig.h to stay ahead.\n//-----------------------------------------------------------------------------\n\nnamespace ImGui\n{\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    IMGUI_API ImGuiKey     GetKeyIndex(ImGuiKey key);  // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key]\n#else\n    static inline ImGuiKey GetKeyIndex(ImGuiKey key)   { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && \"ImGuiKey and native_index was merged together and native_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey.\"); return key; }\n#endif\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\nnamespace ImGui\n{\n    // OBSOLETED in 1.90.0 (from September 2023)\n    static inline bool  BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags window_flags = 0)                 { return BeginChild(id, size, ImGuiChildFlags_FrameStyle, window_flags); }\n    static inline void  EndChildFrame()                                                                                    { EndChild(); }\n    //static inline bool BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags){ return BeginChild(str_id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags); } // Unnecessary as true == ImGuiChildFlags_Border\n    //static inline bool BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags window_flags)        { return BeginChild(id, size_arg, border ? ImGuiChildFlags_Border : ImGuiChildFlags_None, window_flags);     } // Unnecessary as true == ImGuiChildFlags_Border\n    static inline void  ShowStackToolWindow(bool* p_open = NULL)                            { ShowIDStackToolWindow(p_open); }\n    IMGUI_API bool      ListBox(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int height_in_items = -1);\n    IMGUI_API bool      Combo(const char* label, int* current_item, bool (*old_callback)(void* user_data, int idx, const char** out_text), void* user_data, int items_count, int popup_max_height_in_items = -1);\n    // OBSOLETED in 1.89.7 (from June 2023)\n    IMGUI_API void      SetItemAllowOverlap();                                              // Use SetNextItemAllowOverlap() before item.\n    // OBSOLETED in 1.89.4 (from March 2023)\n    static inline void  PushAllowKeyboardFocus(bool tab_stop)                               { PushTabStop(tab_stop); }\n    static inline void  PopAllowKeyboardFocus()                                             { PopTabStop(); }\n    // OBSOLETED in 1.89 (from August 2022)\n    IMGUI_API bool      ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Use new ImageButton() signature (explicit item id, regular FramePadding)\n    // OBSOLETED in 1.88 (from May 2022)\n    static inline void  CaptureKeyboardFromApp(bool want_capture_keyboard = true)           { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value.\n    static inline void  CaptureMouseFromApp(bool want_capture_mouse = true)                 { SetNextFrameWantCaptureMouse(want_capture_mouse); }       // Renamed as name was misleading + removed default value.\n    // OBSOLETED in 1.86 (from November 2021)\n    IMGUI_API void      CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Calculate coarse clipping for large list of evenly sized items. Prefer using ImGuiListClipper.\n\n    // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE)\n    //-- OBSOLETED in 1.85 (from August 2021)\n    //static inline float GetWindowContentRegionWidth()                                               { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; }\n    //-- OBSOLETED in 1.81 (from February 2021)\n    //static inline bool  ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0))         { return BeginListBox(label, size); }\n    //static inline bool  ListBoxHeader(const char* label, int items_count, int height_in_items = -1) { float height = GetTextLineHeightWithSpacing() * ((height_in_items < 0 ? ImMin(items_count, 7) : height_in_items) + 0.25f) + GetStyle().FramePadding.y * 2.0f; return BeginListBox(label, ImVec2(0.0f, height)); } // Helper to calculate size from items_count and height_in_items\n    //static inline void  ListBoxFooter()                                                             { EndListBox(); }\n    //-- OBSOLETED in 1.79 (from August 2020)\n    //static inline void  OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1)    { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry!\n    //-- OBSOLETED in 1.78 (from June 2020): Old drag/sliders functions that took a 'float power > 1.0f' argument instead of ImGuiSliderFlags_Logarithmic. See github.com/ocornut/imgui/issues/3361 for details.\n    //IMGUI_API bool      DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f)                                                            // OBSOLETED in 1.78 (from June 2020)\n    //IMGUI_API bool      DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f);                                          // OBSOLETED in 1.78 (from June 2020)\n    //IMGUI_API bool      SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power = 1.0f);                                                                        // OBSOLETED in 1.78 (from June 2020)\n    //IMGUI_API bool      SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format, float power = 1.0f);                                                       // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power = 1.0f)    { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); }     // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power = 1.0f)                 { return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); }            // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power = 1.0f)              { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); }        // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power = 1.0f)              { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); }        // OBSOLETED in 1.78 (from June 2020)\n    //static inline bool  SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power = 1.0f)              { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); }        // OBSOLETED in 1.78 (from June 2020)\n    //-- OBSOLETED in 1.77 and before\n    //static inline bool  BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } // OBSOLETED in 1.77 (from June 2020)\n    //static inline void  TreeAdvanceToLabelPos()               { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); }   // OBSOLETED in 1.72 (from July 2019)\n    //static inline void  SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); }                       // OBSOLETED in 1.71 (from June 2019)\n    //static inline float GetContentRegionAvailWidth()          { return GetContentRegionAvail().x; }                               // OBSOLETED in 1.70 (from May 2019)\n    //static inline ImDrawList* GetOverlayDrawList()            { return GetForegroundDrawList(); }                                 // OBSOLETED in 1.69 (from Mar 2019)\n    //static inline void  SetScrollHere(float ratio = 0.5f)     { SetScrollHereY(ratio); }                                          // OBSOLETED in 1.66 (from Nov 2018)\n    //static inline bool  IsItemDeactivatedAfterChange()        { return IsItemDeactivatedAfterEdit(); }                            // OBSOLETED in 1.63 (from Aug 2018)\n    //-- OBSOLETED in 1.60 and before\n    //static inline bool  IsAnyWindowFocused()                  { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); }            // OBSOLETED in 1.60 (from Apr 2018)\n    //static inline bool  IsAnyWindowHovered()                  { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); }            // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018)\n    //static inline void  ShowTestWindow()                      { return ShowDemoWindow(); }                                        // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)\n    //static inline bool  IsRootWindowFocused()                 { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); }           // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)\n    //static inline bool  IsRootWindowOrAnyChildFocused()       { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); }  // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)\n    //static inline void  SetNextWindowContentWidth(float w)    { SetNextWindowContentSize(ImVec2(w, 0.0f)); }                      // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)\n    //static inline float GetItemsLineHeightWithSpacing()       { return GetFrameHeightWithSpacing(); }                             // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017)\n    //IMGUI_API bool      Begin(char* name, bool* p_open, ImVec2 size_first_use, float bg_alpha = -1.0f, ImGuiWindowFlags flags=0); // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017): Equivalent of using SetNextWindowSize(size, ImGuiCond_FirstUseEver) and SetNextWindowBgAlpha().\n    //static inline bool  IsRootWindowOrAnyChildHovered()       { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); }  // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017)\n    //static inline void  AlignFirstTextHeightToWidgets()       { AlignTextToFramePadding(); }                                      // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017)\n    //static inline void  SetNextWindowPosCenter(ImGuiCond c=0) { SetNextWindowPos(GetMainViewport()->GetCenter(), c, ImVec2(0.5f,0.5f)); } // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017)\n    //static inline bool  IsItemHoveredRect()                   { return IsItemHovered(ImGuiHoveredFlags_RectOnly); }               // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017)\n    //static inline bool  IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; }                                     // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017): This was misleading and partly broken. You probably want to use the io.WantCaptureMouse flag instead.\n    //static inline bool  IsMouseHoveringAnyWindow()            { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); }            // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017)\n    //static inline bool  IsMouseHoveringWindow()               { return IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); }       // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017)\n    //-- OBSOLETED in 1.50 and before\n    //static inline bool  CollapsingHeader(char* label, const char* str_id, bool framed = true, bool default_open = false) { return CollapsingHeader(label, (default_open ? (1 << 5) : 0)); } // OBSOLETED in 1.49\n    //static inline ImFont*GetWindowFont()                      { return GetFont(); }                                               // OBSOLETED in 1.48\n    //static inline float GetWindowFontSize()                   { return GetFontSize(); }                                           // OBSOLETED in 1.48\n    //static inline void  SetScrollPosHere()                    { SetScrollHere(); }                                                // OBSOLETED in 1.42\n}\n\n//-- OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect()\n//typedef ImDrawFlags ImDrawCornerFlags;\n//enum ImDrawCornerFlags_\n//{\n//    ImDrawCornerFlags_None      = ImDrawFlags_RoundCornersNone,         // Was == 0 prior to 1.82, this is now == ImDrawFlags_RoundCornersNone which is != 0 and not implicit\n//    ImDrawCornerFlags_TopLeft   = ImDrawFlags_RoundCornersTopLeft,      // Was == 0x01 (1 << 0) prior to 1.82. Order matches ImDrawFlags_NoRoundCorner* flag (we exploit this internally).\n//    ImDrawCornerFlags_TopRight  = ImDrawFlags_RoundCornersTopRight,     // Was == 0x02 (1 << 1) prior to 1.82.\n//    ImDrawCornerFlags_BotLeft   = ImDrawFlags_RoundCornersBottomLeft,   // Was == 0x04 (1 << 2) prior to 1.82.\n//    ImDrawCornerFlags_BotRight  = ImDrawFlags_RoundCornersBottomRight,  // Was == 0x08 (1 << 3) prior to 1.82.\n//    ImDrawCornerFlags_All       = ImDrawFlags_RoundCornersAll,          // Was == 0x0F prior to 1.82\n//    ImDrawCornerFlags_Top       = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight,\n//    ImDrawCornerFlags_Bot       = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight,\n//    ImDrawCornerFlags_Left      = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft,\n//    ImDrawCornerFlags_Right     = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight,\n//};\n\n// RENAMED and MERGED both ImGuiKey_ModXXX and ImGuiModFlags_XXX into ImGuiMod_XXX (from September 2022)\n// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obscolescence schedule to reduce confusion and because they were not meant to be used in the first place.\ntypedef ImGuiKeyChord ImGuiModFlags;      // == int. We generally use ImGuiKeyChord to mean \"a ImGuiKey or-ed with any number of ImGuiMod_XXX value\", but you may store only mods in there.\nenum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiModFlags_Shift = ImGuiMod_Shift, ImGuiModFlags_Alt = ImGuiMod_Alt, ImGuiModFlags_Super = ImGuiMod_Super };\n//typedef ImGuiKeyChord ImGuiKeyModFlags; // == int\n//enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = 0, ImGuiKeyModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiKeyModFlags_Shift = ImGuiMod_Shift, ImGuiKeyModFlags_Alt = ImGuiMod_Alt, ImGuiKeyModFlags_Super = ImGuiMod_Super };\n\n#define IM_OFFSETOF(_TYPE,_MEMBER)  offsetof(_TYPE, _MEMBER)    // OBSOLETED IN 1.90 (now using C++11 standard version)\n\n#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n\n// RENAMED IMGUI_DISABLE_METRICS_WINDOW > IMGUI_DISABLE_DEBUG_TOOLS in 1.88 (from June 2022)\n#if defined(IMGUI_DISABLE_METRICS_WINDOW) && !defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && !defined(IMGUI_DISABLE_DEBUG_TOOLS)\n#define IMGUI_DISABLE_DEBUG_TOOLS\n#endif\n#if defined(IMGUI_DISABLE_METRICS_WINDOW) && defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS)\n#error IMGUI_DISABLE_METRICS_WINDOW was renamed to IMGUI_DISABLE_DEBUG_TOOLS, please use new name.\n#endif\n\n//-----------------------------------------------------------------------------\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#elif defined(__GNUC__)\n#pragma GCC diagnostic pop\n#endif\n\n#ifdef _MSC_VER\n#pragma warning (pop)\n#endif\n\n// Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h)\n#ifdef IMGUI_INCLUDE_IMGUI_USER_H\n#include \"imgui_user.h\"\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "third-party/imgui/imgui_demo.cpp",
    "content": "// dear imgui, v1.90.0\n// (demo code)\n\n// Help:\n// - Read FAQ at http://dearimgui.com/faq\n// - Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp. All applications in examples/ are doing that.\n// - Need help integrating Dear ImGui in your codebase?\n//   - Read Getting Started https://github.com/ocornut/imgui/wiki/Getting-Started\n//   - Read 'Programmer guide' in imgui.cpp for notes on how to setup Dear ImGui in your codebase.\n// Read imgui.cpp for more details, documentation and comments.\n// Get the latest version at https://github.com/ocornut/imgui\n\n//---------------------------------------------------\n// PLEASE DO NOT REMOVE THIS FILE FROM YOUR PROJECT!\n//---------------------------------------------------\n// Message to the person tempted to delete this file when integrating Dear ImGui into their codebase:\n// Think again! It is the most useful reference code that you and other coders will want to refer to and call.\n// Have the ImGui::ShowDemoWindow() function wired in an always-available debug menu of your game/app!\n// Also include Metrics! ItemPicker! DebugLog! and other debug features.\n// Removing this file from your project is hindering access to documentation for everyone in your team,\n// likely leading you to poorer usage of the library.\n// Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow().\n// If you want to link core Dear ImGui in your shipped builds but want a thorough guarantee that the demo will not be\n// linked, you can setup your imconfig.h with #define IMGUI_DISABLE_DEMO_WINDOWS and those functions will be empty.\n// In another situation, whenever you have Dear ImGui available you probably want this to be available for reference.\n// Thank you,\n// -Your beloved friend, imgui_demo.cpp (which you won't delete)\n\n//--------------------------------------------\n// ABOUT THE MEANING OF THE 'static' KEYWORD:\n//--------------------------------------------\n// In this demo code, we frequently use 'static' variables inside functions.\n// A static variable persists across calls. It is essentially a global variable but declared inside the scope of the function.\n// Think of \"static int n = 0;\" as \"global int n = 0;\" !\n// We do this IN THE DEMO because we want:\n// - to gather code and data in the same place.\n// - to make the demo source code faster to read, faster to change, smaller in size.\n// - it is also a convenient way of storing simple UI related information as long as your function\n//   doesn't need to be reentrant or used in multiple threads.\n// This might be a pattern you will want to use in your code, but most of the data you would be working\n// with in a complex codebase is likely going to be stored outside your functions.\n\n//-----------------------------------------\n// ABOUT THE CODING STYLE OF OUR DEMO CODE\n//-----------------------------------------\n// The Demo code in this file is designed to be easy to copy-and-paste into your application!\n// Because of this:\n// - We never omit the ImGui:: prefix when calling functions, even though most code here is in the same namespace.\n// - We try to declare static variables in the local scope, as close as possible to the code using them.\n// - We never use any of the helpers/facilities used internally by Dear ImGui, unless available in the public API.\n// - We never use maths operators on ImVec2/ImVec4. For our other sources files we use them, and they are provided\n//   by imgui.h using the IMGUI_DEFINE_MATH_OPERATORS define. For your own sources file they are optional\n//   and require you either enable those, either provide your own via IM_VEC2_CLASS_EXTRA in imconfig.h.\n//   Because we can't assume anything about your support of maths operators, we cannot use them in imgui_demo.cpp.\n\n// Navigating this file:\n// - In Visual Studio IDE: CTRL+comma (\"Edit.GoToAll\") can follow symbols in comments, whereas CTRL+F12 (\"Edit.GoToImplementation\") cannot.\n// - With Visual Assist installed: ALT+G (\"VAssistX.GoToImplementation\") can also follow symbols in comments.\n\n/*\n\nIndex of this file:\n\n// [SECTION] Forward Declarations\n// [SECTION] Helpers\n// [SECTION] Demo Window / ShowDemoWindow()\n// - ShowDemoWindow()\n// - sub section: ShowDemoWindowWidgets()\n// - sub section: ShowDemoWindowLayout()\n// - sub section: ShowDemoWindowPopups()\n// - sub section: ShowDemoWindowTables()\n// - sub section: ShowDemoWindowInputs()\n// [SECTION] About Window / ShowAboutWindow()\n// [SECTION] Style Editor / ShowStyleEditor()\n// [SECTION] User Guide / ShowUserGuide()\n// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()\n// [SECTION] Example App: Debug Console / ShowExampleAppConsole()\n// [SECTION] Example App: Debug Log / ShowExampleAppLog()\n// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()\n// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()\n// [SECTION] Example App: Long Text / ShowExampleAppLongText()\n// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()\n// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()\n// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()\n// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()\n// [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles()\n// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()\n// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()\n\n*/\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n\n// System includes\n#include <ctype.h>          // toupper\n#include <limits.h>         // INT_MIN, INT_MAX\n#include <math.h>           // sqrtf, powf, cosf, sinf, floorf, ceilf\n#include <stdio.h>          // vsnprintf, sscanf, printf\n#include <stdlib.h>         // NULL, malloc, free, atoi\n#include <stdint.h>         // intptr_t\n#if !defined(_MSC_VER) || _MSC_VER >= 1800\n#include <inttypes.h>       // PRId64/PRIu64, not avail in some MinGW headers.\n#endif\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4127)     // condition expression is constant\n#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'                     // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast                           // yes, they are more terse.\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"        // warning: 'xx' is deprecated: The POSIX name for this..   // for strdup used in demo code (so user can copy & paste the code)\n#pragma clang diagnostic ignored \"-Wint-to-void-pointer-cast\"       // warning: cast to 'void *' from smaller integer type\n#pragma clang diagnostic ignored \"-Wformat-security\"                // warning: format string is not a string literal\n#pragma clang diagnostic ignored \"-Wexit-time-destructors\"          // warning: declaration requires an exit-time destructor    // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals.\n#pragma clang diagnostic ignored \"-Wunused-macros\"                  // warning: macro is not used                               // we define snprintf/vsnprintf on Windows so they are available, but not always used.\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant                   // some standard header variations use #define NULL 0\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.\n#pragma clang diagnostic ignored \"-Wreserved-id-macro\"              // warning: macro name is a reserved identifier\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#elif defined(__GNUC__)\n#pragma GCC diagnostic ignored \"-Wpragmas\"                  // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wint-to-pointer-cast\"      // warning: cast to pointer from integer of different size\n#pragma GCC diagnostic ignored \"-Wformat-security\"          // warning: format string is not a string literal (potentially insecure)\n#pragma GCC diagnostic ignored \"-Wdouble-promotion\"         // warning: implicit conversion from 'float' to 'double' when passing argument to function\n#pragma GCC diagnostic ignored \"-Wconversion\"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value\n#pragma GCC diagnostic ignored \"-Wmisleading-indentation\"   // [__GNUC__ >= 6] warning: this 'if' clause does not guard this statement      // GCC 6.0+ only. See #883 on GitHub.\n#endif\n\n// Play it nice with Windows users (Update: May 2018, Notepad now supports Unix-style carriage returns!)\n#ifdef _WIN32\n#define IM_NEWLINE  \"\\r\\n\"\n#else\n#define IM_NEWLINE  \"\\n\"\n#endif\n\n// Helpers\n#if defined(_MSC_VER) && !defined(snprintf)\n#define snprintf    _snprintf\n#endif\n#if defined(_MSC_VER) && !defined(vsnprintf)\n#define vsnprintf   _vsnprintf\n#endif\n\n// Format specifiers for 64-bit values (hasn't been decently standardized before VS2013)\n#if !defined(PRId64) && defined(_MSC_VER)\n#define PRId64 \"I64d\"\n#define PRIu64 \"I64u\"\n#elif !defined(PRId64)\n#define PRId64 \"lld\"\n#define PRIu64 \"llu\"\n#endif\n\n// Helpers macros\n// We normally try to not use many helpers in imgui_demo.cpp in order to make code easier to copy and paste,\n// but making an exception here as those are largely simplifying code...\n// In other imgui sources we can use nicer internal functions from imgui_internal.h (ImMin/ImMax) but not in the demo.\n#define IM_MIN(A, B)            (((A) < (B)) ? (A) : (B))\n#define IM_MAX(A, B)            (((A) >= (B)) ? (A) : (B))\n#define IM_CLAMP(V, MN, MX)     ((V) < (MN) ? (MN) : (V) > (MX) ? (MX) : (V))\n\n// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall\n#ifndef IMGUI_CDECL\n#ifdef _MSC_VER\n#define IMGUI_CDECL __cdecl\n#else\n#define IMGUI_CDECL\n#endif\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Forward Declarations, Helpers\n//-----------------------------------------------------------------------------\n\n#if !defined(IMGUI_DISABLE_DEMO_WINDOWS)\n\n// Forward Declarations\nstatic void ShowExampleAppMainMenuBar();\nstatic void ShowExampleAppConsole(bool* p_open);\nstatic void ShowExampleAppCustomRendering(bool* p_open);\nstatic void ShowExampleAppDocuments(bool* p_open);\nstatic void ShowExampleAppLog(bool* p_open);\nstatic void ShowExampleAppLayout(bool* p_open);\nstatic void ShowExampleAppPropertyEditor(bool* p_open);\nstatic void ShowExampleAppSimpleOverlay(bool* p_open);\nstatic void ShowExampleAppAutoResize(bool* p_open);\nstatic void ShowExampleAppConstrainedResize(bool* p_open);\nstatic void ShowExampleAppFullscreen(bool* p_open);\nstatic void ShowExampleAppLongText(bool* p_open);\nstatic void ShowExampleAppWindowTitles(bool* p_open);\nstatic void ShowExampleMenuFile();\n\n// We split the contents of the big ShowDemoWindow() function into smaller functions\n// (because the link time of very large functions grow non-linearly)\nstatic void ShowDemoWindowWidgets();\nstatic void ShowDemoWindowLayout();\nstatic void ShowDemoWindowPopups();\nstatic void ShowDemoWindowTables();\nstatic void ShowDemoWindowColumns();\nstatic void ShowDemoWindowInputs();\n\n//-----------------------------------------------------------------------------\n// [SECTION] Helpers\n//-----------------------------------------------------------------------------\n\n// Helper to display a little (?) mark which shows a tooltip when hovered.\n// In your own code you may want to display an actual icon if you are using a merged icon fonts (see docs/FONTS.md)\nstatic void HelpMarker(const char* desc)\n{\n    ImGui::TextDisabled(\"(?)\");\n    if (ImGui::BeginItemTooltip())\n    {\n        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);\n        ImGui::TextUnformatted(desc);\n        ImGui::PopTextWrapPos();\n        ImGui::EndTooltip();\n    }\n}\n\n// Helper to wire demo markers located in code to an interactive browser\ntypedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data);\nextern ImGuiDemoMarkerCallback      GImGuiDemoMarkerCallback;\nextern void*                        GImGuiDemoMarkerCallbackUserData;\nImGuiDemoMarkerCallback             GImGuiDemoMarkerCallback = NULL;\nvoid*                               GImGuiDemoMarkerCallbackUserData = NULL;\n#define IMGUI_DEMO_MARKER(section)  do { if (GImGuiDemoMarkerCallback != NULL) GImGuiDemoMarkerCallback(__FILE__, __LINE__, section, GImGuiDemoMarkerCallbackUserData); } while (0)\n\n//-----------------------------------------------------------------------------\n// [SECTION] Demo Window / ShowDemoWindow()\n//-----------------------------------------------------------------------------\n// - ShowDemoWindow()\n// - ShowDemoWindowWidgets()\n// - ShowDemoWindowLayout()\n// - ShowDemoWindowPopups()\n// - ShowDemoWindowTables()\n// - ShowDemoWindowColumns()\n// - ShowDemoWindowInputs()\n//-----------------------------------------------------------------------------\n\n// Demonstrate most Dear ImGui features (this is big function!)\n// You may execute this function to experiment with the UI and understand what it does.\n// You may then search for keywords in the code when you are interested by a specific feature.\nvoid ImGui::ShowDemoWindow(bool* p_open)\n{\n    // Exceptionally add an extra assert here for people confused about initial Dear ImGui setup\n    // Most functions would normally just assert/crash if the context is missing.\n    IM_ASSERT(ImGui::GetCurrentContext() != NULL && \"Missing Dear ImGui context. Refer to examples app!\");\n\n    // Examples Apps (accessible from the \"Examples\" menu)\n    static bool show_app_main_menu_bar = false;\n    static bool show_app_console = false;\n    static bool show_app_custom_rendering = false;\n    static bool show_app_documents = false;\n    static bool show_app_log = false;\n    static bool show_app_layout = false;\n    static bool show_app_property_editor = false;\n    static bool show_app_simple_overlay = false;\n    static bool show_app_auto_resize = false;\n    static bool show_app_constrained_resize = false;\n    static bool show_app_fullscreen = false;\n    static bool show_app_long_text = false;\n    static bool show_app_window_titles = false;\n\n    if (show_app_main_menu_bar)       ShowExampleAppMainMenuBar();\n    if (show_app_documents)           ShowExampleAppDocuments(&show_app_documents);\n    if (show_app_console)             ShowExampleAppConsole(&show_app_console);\n    if (show_app_custom_rendering)    ShowExampleAppCustomRendering(&show_app_custom_rendering);\n    if (show_app_log)                 ShowExampleAppLog(&show_app_log);\n    if (show_app_layout)              ShowExampleAppLayout(&show_app_layout);\n    if (show_app_property_editor)     ShowExampleAppPropertyEditor(&show_app_property_editor);\n    if (show_app_simple_overlay)      ShowExampleAppSimpleOverlay(&show_app_simple_overlay);\n    if (show_app_auto_resize)         ShowExampleAppAutoResize(&show_app_auto_resize);\n    if (show_app_constrained_resize)  ShowExampleAppConstrainedResize(&show_app_constrained_resize);\n    if (show_app_fullscreen)          ShowExampleAppFullscreen(&show_app_fullscreen);\n    if (show_app_long_text)           ShowExampleAppLongText(&show_app_long_text);\n    if (show_app_window_titles)       ShowExampleAppWindowTitles(&show_app_window_titles);\n\n    // Dear ImGui Tools (accessible from the \"Tools\" menu)\n    static bool show_tool_metrics = false;\n    static bool show_tool_debug_log = false;\n    static bool show_tool_id_stack_tool = false;\n    static bool show_tool_style_editor = false;\n    static bool show_tool_about = false;\n\n    if (show_tool_metrics)\n        ImGui::ShowMetricsWindow(&show_tool_metrics);\n    if (show_tool_debug_log)\n        ImGui::ShowDebugLogWindow(&show_tool_debug_log);\n    if (show_tool_id_stack_tool)\n        ImGui::ShowIDStackToolWindow(&show_tool_id_stack_tool);\n    if (show_tool_style_editor)\n    {\n        ImGui::Begin(\"Dear ImGui Style Editor\", &show_tool_style_editor);\n        ImGui::ShowStyleEditor();\n        ImGui::End();\n    }\n    if (show_tool_about)\n        ImGui::ShowAboutWindow(&show_tool_about);\n\n    // Demonstrate the various window flags. Typically you would just use the default!\n    static bool no_titlebar = false;\n    static bool no_scrollbar = false;\n    static bool no_menu = false;\n    static bool no_move = false;\n    static bool no_resize = false;\n    static bool no_collapse = false;\n    static bool no_close = false;\n    static bool no_nav = false;\n    static bool no_background = false;\n    static bool no_bring_to_front = false;\n    static bool unsaved_document = false;\n\n    ImGuiWindowFlags window_flags = 0;\n    if (no_titlebar)        window_flags |= ImGuiWindowFlags_NoTitleBar;\n    if (no_scrollbar)       window_flags |= ImGuiWindowFlags_NoScrollbar;\n    if (!no_menu)           window_flags |= ImGuiWindowFlags_MenuBar;\n    if (no_move)            window_flags |= ImGuiWindowFlags_NoMove;\n    if (no_resize)          window_flags |= ImGuiWindowFlags_NoResize;\n    if (no_collapse)        window_flags |= ImGuiWindowFlags_NoCollapse;\n    if (no_nav)             window_flags |= ImGuiWindowFlags_NoNav;\n    if (no_background)      window_flags |= ImGuiWindowFlags_NoBackground;\n    if (no_bring_to_front)  window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus;\n    if (unsaved_document)   window_flags |= ImGuiWindowFlags_UnsavedDocument;\n    if (no_close)           p_open = NULL; // Don't pass our bool* to Begin\n\n    // We specify a default position/size in case there's no data in the .ini file.\n    // We only do it to make the demo applications a little more welcoming, but typically this isn't required.\n    const ImGuiViewport* main_viewport = ImGui::GetMainViewport();\n    ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver);\n    ImGui::SetNextWindowSize(ImVec2(550, 680), ImGuiCond_FirstUseEver);\n\n    // Main body of the Demo window starts here.\n    if (!ImGui::Begin(\"Dear ImGui Demo\", p_open, window_flags))\n    {\n        // Early out if the window is collapsed, as an optimization.\n        ImGui::End();\n        return;\n    }\n\n    // Most \"big\" widgets share a common width settings by default. See 'Demo->Layout->Widgets Width' for details.\n    // e.g. Use 2/3 of the space for widgets and 1/3 for labels (right align)\n    //ImGui::PushItemWidth(-ImGui::GetWindowWidth() * 0.35f);\n    // e.g. Leave a fixed amount of width for labels (by passing a negative value), the rest goes to widgets.\n    ImGui::PushItemWidth(ImGui::GetFontSize() * -12);\n\n    // Menu Bar\n    if (ImGui::BeginMenuBar())\n    {\n        if (ImGui::BeginMenu(\"Menu\"))\n        {\n            IMGUI_DEMO_MARKER(\"Menu/File\");\n            ShowExampleMenuFile();\n            ImGui::EndMenu();\n        }\n        if (ImGui::BeginMenu(\"Examples\"))\n        {\n            IMGUI_DEMO_MARKER(\"Menu/Examples\");\n            ImGui::MenuItem(\"Main menu bar\", NULL, &show_app_main_menu_bar);\n\n            ImGui::SeparatorText(\"Mini apps\");\n            ImGui::MenuItem(\"Console\", NULL, &show_app_console);\n            ImGui::MenuItem(\"Custom rendering\", NULL, &show_app_custom_rendering);\n            ImGui::MenuItem(\"Documents\", NULL, &show_app_documents);\n            ImGui::MenuItem(\"Log\", NULL, &show_app_log);\n            ImGui::MenuItem(\"Property editor\", NULL, &show_app_property_editor);\n            ImGui::MenuItem(\"Simple layout\", NULL, &show_app_layout);\n            ImGui::MenuItem(\"Simple overlay\", NULL, &show_app_simple_overlay);\n\n            ImGui::SeparatorText(\"Concepts\");\n            ImGui::MenuItem(\"Auto-resizing window\", NULL, &show_app_auto_resize);\n            ImGui::MenuItem(\"Constrained-resizing window\", NULL, &show_app_constrained_resize);\n            ImGui::MenuItem(\"Fullscreen window\", NULL, &show_app_fullscreen);\n            ImGui::MenuItem(\"Long text display\", NULL, &show_app_long_text);\n            ImGui::MenuItem(\"Manipulating window titles\", NULL, &show_app_window_titles);\n\n            ImGui::EndMenu();\n        }\n        //if (ImGui::MenuItem(\"MenuItem\")) {} // You can also use MenuItem() inside a menu bar!\n        if (ImGui::BeginMenu(\"Tools\"))\n        {\n            IMGUI_DEMO_MARKER(\"Menu/Tools\");\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n            const bool has_debug_tools = true;\n#else\n            const bool has_debug_tools = false;\n#endif\n            ImGui::MenuItem(\"Metrics/Debugger\", NULL, &show_tool_metrics, has_debug_tools);\n            ImGui::MenuItem(\"Debug Log\", NULL, &show_tool_debug_log, has_debug_tools);\n            ImGui::MenuItem(\"ID Stack Tool\", NULL, &show_tool_id_stack_tool, has_debug_tools);\n            ImGui::MenuItem(\"Style Editor\", NULL, &show_tool_style_editor);\n            ImGui::MenuItem(\"About Dear ImGui\", NULL, &show_tool_about);\n            ImGui::EndMenu();\n        }\n        ImGui::EndMenuBar();\n    }\n\n    ImGui::Text(\"dear imgui says hello! (%s) (%d)\", IMGUI_VERSION, IMGUI_VERSION_NUM);\n    ImGui::Spacing();\n\n    IMGUI_DEMO_MARKER(\"Help\");\n    if (ImGui::CollapsingHeader(\"Help\"))\n    {\n        ImGui::SeparatorText(\"ABOUT THIS DEMO:\");\n        ImGui::BulletText(\"Sections below are demonstrating many aspects of the library.\");\n        ImGui::BulletText(\"The \\\"Examples\\\" menu above leads to more demo contents.\");\n        ImGui::BulletText(\"The \\\"Tools\\\" menu above gives access to: About Box, Style Editor,\\n\"\n                          \"and Metrics/Debugger (general purpose Dear ImGui debugging tool).\");\n\n        ImGui::SeparatorText(\"PROGRAMMER GUIDE:\");\n        ImGui::BulletText(\"See the ShowDemoWindow() code in imgui_demo.cpp. <- you are here!\");\n        ImGui::BulletText(\"See comments in imgui.cpp.\");\n        ImGui::BulletText(\"See example applications in the examples/ folder.\");\n        ImGui::BulletText(\"Read the FAQ at https://www.dearimgui.com/faq/\");\n        ImGui::BulletText(\"Set 'io.ConfigFlags |= NavEnableKeyboard' for keyboard controls.\");\n        ImGui::BulletText(\"Set 'io.ConfigFlags |= NavEnableGamepad' for gamepad controls.\");\n\n        ImGui::SeparatorText(\"USER GUIDE:\");\n        ImGui::ShowUserGuide();\n    }\n\n    IMGUI_DEMO_MARKER(\"Configuration\");\n    if (ImGui::CollapsingHeader(\"Configuration\"))\n    {\n        ImGuiIO& io = ImGui::GetIO();\n\n        if (ImGui::TreeNode(\"Configuration##2\"))\n        {\n            ImGui::SeparatorText(\"General\");\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NavEnableKeyboard\",    &io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard);\n            ImGui::SameLine(); HelpMarker(\"Enable keyboard controls.\");\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NavEnableGamepad\",     &io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad);\n            ImGui::SameLine(); HelpMarker(\"Enable gamepad controls. Require backend to set io.BackendFlags |= ImGuiBackendFlags_HasGamepad.\\n\\nRead instructions in imgui.cpp for details.\");\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NavEnableSetMousePos\", &io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos);\n            ImGui::SameLine(); HelpMarker(\"Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos.\");\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NoMouse\",              &io.ConfigFlags, ImGuiConfigFlags_NoMouse);\n            if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)\n            {\n                // The \"NoMouse\" option can get us stuck with a disabled mouse! Let's provide an alternative way to fix it:\n                if (fmodf((float)ImGui::GetTime(), 0.40f) < 0.20f)\n                {\n                    ImGui::SameLine();\n                    ImGui::Text(\"<<PRESS SPACE TO DISABLE>>\");\n                }\n                if (ImGui::IsKeyPressed(ImGuiKey_Space))\n                    io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;\n            }\n            ImGui::CheckboxFlags(\"io.ConfigFlags: NoMouseCursorChange\", &io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange);\n            ImGui::SameLine(); HelpMarker(\"Instruct backend to not alter mouse cursor shape and visibility.\");\n            ImGui::Checkbox(\"io.ConfigInputTrickleEventQueue\", &io.ConfigInputTrickleEventQueue);\n            ImGui::SameLine(); HelpMarker(\"Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates.\");\n            ImGui::Checkbox(\"io.MouseDrawCursor\", &io.MouseDrawCursor);\n            ImGui::SameLine(); HelpMarker(\"Instruct Dear ImGui to render a mouse cursor itself. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\\n\\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something).\");\n\n            ImGui::SeparatorText(\"Widgets\");\n            ImGui::Checkbox(\"io.ConfigInputTextCursorBlink\", &io.ConfigInputTextCursorBlink);\n            ImGui::SameLine(); HelpMarker(\"Enable blinking cursor (optional as some users consider it to be distracting).\");\n            ImGui::Checkbox(\"io.ConfigInputTextEnterKeepActive\", &io.ConfigInputTextEnterKeepActive);\n            ImGui::SameLine(); HelpMarker(\"Pressing Enter will keep item active and select contents (single-line only).\");\n            ImGui::Checkbox(\"io.ConfigDragClickToInputText\", &io.ConfigDragClickToInputText);\n            ImGui::SameLine(); HelpMarker(\"Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving).\");\n            ImGui::Checkbox(\"io.ConfigWindowsResizeFromEdges\", &io.ConfigWindowsResizeFromEdges);\n            ImGui::SameLine(); HelpMarker(\"Enable resizing of windows from their edges and from the lower-left corner.\\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback.\");\n            ImGui::Checkbox(\"io.ConfigWindowsMoveFromTitleBarOnly\", &io.ConfigWindowsMoveFromTitleBarOnly);\n            ImGui::Checkbox(\"io.ConfigMacOSXBehaviors\", &io.ConfigMacOSXBehaviors);\n            ImGui::Text(\"Also see Style->Rendering for rendering options.\");\n\n            ImGui::SeparatorText(\"Debug\");\n            ImGui::BeginDisabled();\n            ImGui::Checkbox(\"io.ConfigDebugBeginReturnValueOnce\", &io.ConfigDebugBeginReturnValueOnce); // .\n            ImGui::EndDisabled();\n            ImGui::SameLine(); HelpMarker(\"First calls to Begin()/BeginChild() will return false.\\n\\nTHIS OPTION IS DISABLED because it needs to be set at application boot-time to make sense. Showing the disabled option is a way to make this feature easier to discover\");\n            ImGui::Checkbox(\"io.ConfigDebugBeginReturnValueLoop\", &io.ConfigDebugBeginReturnValueLoop);\n            ImGui::SameLine(); HelpMarker(\"Some calls to Begin()/BeginChild() will return false.\\n\\nWill cycle through window depths then repeat. Windows should be flickering while running.\");\n            ImGui::Checkbox(\"io.ConfigDebugIgnoreFocusLoss\", &io.ConfigDebugIgnoreFocusLoss);\n            ImGui::SameLine(); HelpMarker(\"Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data.\");\n            ImGui::Checkbox(\"io.ConfigDebugIniSettings\", &io.ConfigDebugIniSettings);\n            ImGui::SameLine(); HelpMarker(\"Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower).\");\n\n            ImGui::TreePop();\n            ImGui::Spacing();\n        }\n\n        IMGUI_DEMO_MARKER(\"Configuration/Backend Flags\");\n        if (ImGui::TreeNode(\"Backend Flags\"))\n        {\n            HelpMarker(\n                \"Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\\n\"\n                \"Here we expose them as read-only fields to avoid breaking interactions with your backend.\");\n\n            // FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright?\n            ImGui::BeginDisabled();\n            ImGui::CheckboxFlags(\"io.BackendFlags: HasGamepad\",           &io.BackendFlags, ImGuiBackendFlags_HasGamepad);\n            ImGui::CheckboxFlags(\"io.BackendFlags: HasMouseCursors\",      &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);\n            ImGui::CheckboxFlags(\"io.BackendFlags: HasSetMousePos\",       &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos);\n            ImGui::CheckboxFlags(\"io.BackendFlags: RendererHasVtxOffset\", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset);\n            ImGui::EndDisabled();\n            ImGui::TreePop();\n            ImGui::Spacing();\n        }\n\n        IMGUI_DEMO_MARKER(\"Configuration/Style\");\n        if (ImGui::TreeNode(\"Style\"))\n        {\n            HelpMarker(\"The same contents can be accessed in 'Tools->Style Editor' or by calling the ShowStyleEditor() function.\");\n            ImGui::ShowStyleEditor();\n            ImGui::TreePop();\n            ImGui::Spacing();\n        }\n\n        IMGUI_DEMO_MARKER(\"Configuration/Capture, Logging\");\n        if (ImGui::TreeNode(\"Capture/Logging\"))\n        {\n            HelpMarker(\n                \"The logging API redirects all text output so you can easily capture the content of \"\n                \"a window or a block. Tree nodes can be automatically expanded.\\n\"\n                \"Try opening any of the contents below in this window and then click one of the \\\"Log To\\\" button.\");\n            ImGui::LogButtons();\n\n            HelpMarker(\"You can also call ImGui::LogText() to output directly to the log without a visual output.\");\n            if (ImGui::Button(\"Copy \\\"Hello, world!\\\" to clipboard\"))\n            {\n                ImGui::LogToClipboard();\n                ImGui::LogText(\"Hello, world!\");\n                ImGui::LogFinish();\n            }\n            ImGui::TreePop();\n        }\n    }\n\n    IMGUI_DEMO_MARKER(\"Window options\");\n    if (ImGui::CollapsingHeader(\"Window options\"))\n    {\n        if (ImGui::BeginTable(\"split\", 3))\n        {\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No titlebar\", &no_titlebar);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No scrollbar\", &no_scrollbar);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No menu\", &no_menu);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No move\", &no_move);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No resize\", &no_resize);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No collapse\", &no_collapse);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No close\", &no_close);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No nav\", &no_nav);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No background\", &no_background);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"No bring to front\", &no_bring_to_front);\n            ImGui::TableNextColumn(); ImGui::Checkbox(\"Unsaved document\", &unsaved_document);\n            ImGui::EndTable();\n        }\n    }\n\n    // All demo contents\n    ShowDemoWindowWidgets();\n    ShowDemoWindowLayout();\n    ShowDemoWindowPopups();\n    ShowDemoWindowTables();\n    ShowDemoWindowInputs();\n\n    // End of ShowDemoWindow()\n    ImGui::PopItemWidth();\n    ImGui::End();\n}\n\nstatic void ShowDemoWindowWidgets()\n{\n    IMGUI_DEMO_MARKER(\"Widgets\");\n    if (!ImGui::CollapsingHeader(\"Widgets\"))\n        return;\n\n    static bool disable_all = false; // The Checkbox for that is inside the \"Disabled\" section at the bottom\n    if (disable_all)\n        ImGui::BeginDisabled();\n\n    IMGUI_DEMO_MARKER(\"Widgets/Basic\");\n    if (ImGui::TreeNode(\"Basic\"))\n    {\n        ImGui::SeparatorText(\"General\");\n\n        IMGUI_DEMO_MARKER(\"Widgets/Basic/Button\");\n        static int clicked = 0;\n        if (ImGui::Button(\"Button\"))\n            clicked++;\n        if (clicked & 1)\n        {\n            ImGui::SameLine();\n            ImGui::Text(\"Thanks for clicking me!\");\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Basic/Checkbox\");\n        static bool check = true;\n        ImGui::Checkbox(\"checkbox\", &check);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Basic/RadioButton\");\n        static int e = 0;\n        ImGui::RadioButton(\"radio a\", &e, 0); ImGui::SameLine();\n        ImGui::RadioButton(\"radio b\", &e, 1); ImGui::SameLine();\n        ImGui::RadioButton(\"radio c\", &e, 2);\n\n        // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style.\n        IMGUI_DEMO_MARKER(\"Widgets/Basic/Buttons (Colored)\");\n        for (int i = 0; i < 7; i++)\n        {\n            if (i > 0)\n                ImGui::SameLine();\n            ImGui::PushID(i);\n            ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f));\n            ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f));\n            ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f));\n            ImGui::Button(\"Click\");\n            ImGui::PopStyleColor(3);\n            ImGui::PopID();\n        }\n\n        // Use AlignTextToFramePadding() to align text baseline to the baseline of framed widgets elements\n        // (otherwise a Text+SameLine+Button sequence will have the text a little too high by default!)\n        // See 'Demo->Layout->Text Baseline Alignment' for details.\n        ImGui::AlignTextToFramePadding();\n        ImGui::Text(\"Hold to repeat:\");\n        ImGui::SameLine();\n\n        // Arrow buttons with Repeater\n        IMGUI_DEMO_MARKER(\"Widgets/Basic/Buttons (Repeating)\");\n        static int counter = 0;\n        float spacing = ImGui::GetStyle().ItemInnerSpacing.x;\n        ImGui::PushButtonRepeat(true);\n        if (ImGui::ArrowButton(\"##left\", ImGuiDir_Left)) { counter--; }\n        ImGui::SameLine(0.0f, spacing);\n        if (ImGui::ArrowButton(\"##right\", ImGuiDir_Right)) { counter++; }\n        ImGui::PopButtonRepeat();\n        ImGui::SameLine();\n        ImGui::Text(\"%d\", counter);\n\n        ImGui::Button(\"Tooltip\");\n        ImGui::SetItemTooltip(\"I am a tooltip\");\n\n        ImGui::LabelText(\"label\", \"Value\");\n\n        ImGui::SeparatorText(\"Inputs\");\n\n        {\n            // To wire InputText() with std::string or any other custom string type,\n            // see the \"Text Input > Resize Callback\" section of this demo, and the misc/cpp/imgui_stdlib.h file.\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/InputText\");\n            static char str0[128] = \"Hello, world!\";\n            ImGui::InputText(\"input text\", str0, IM_ARRAYSIZE(str0));\n            ImGui::SameLine(); HelpMarker(\n                \"USER:\\n\"\n                \"Hold SHIFT or use mouse to select text.\\n\"\n                \"CTRL+Left/Right to word jump.\\n\"\n                \"CTRL+A or Double-Click to select all.\\n\"\n                \"CTRL+X,CTRL+C,CTRL+V clipboard.\\n\"\n                \"CTRL+Z,CTRL+Y undo/redo.\\n\"\n                \"ESCAPE to revert.\\n\\n\"\n                \"PROGRAMMER:\\n\"\n                \"You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputText() \"\n                \"to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example (this is not demonstrated \"\n                \"in imgui_demo.cpp).\");\n\n            static char str1[128] = \"\";\n            ImGui::InputTextWithHint(\"input text (w/ hint)\", \"enter text here\", str1, IM_ARRAYSIZE(str1));\n\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/InputInt, InputFloat\");\n            static int i0 = 123;\n            ImGui::InputInt(\"input int\", &i0);\n\n            static float f0 = 0.001f;\n            ImGui::InputFloat(\"input float\", &f0, 0.01f, 1.0f, \"%.3f\");\n\n            static double d0 = 999999.00000001;\n            ImGui::InputDouble(\"input double\", &d0, 0.01f, 1.0f, \"%.8f\");\n\n            static float f1 = 1.e10f;\n            ImGui::InputFloat(\"input scientific\", &f1, 0.0f, 0.0f, \"%e\");\n            ImGui::SameLine(); HelpMarker(\n                \"You can input value using the scientific notation,\\n\"\n                \"  e.g. \\\"1e+8\\\" becomes \\\"100000000\\\".\");\n\n            static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f };\n            ImGui::InputFloat3(\"input float3\", vec4a);\n        }\n\n        ImGui::SeparatorText(\"Drags\");\n\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/DragInt, DragFloat\");\n            static int i1 = 50, i2 = 42;\n            ImGui::DragInt(\"drag int\", &i1, 1);\n            ImGui::SameLine(); HelpMarker(\n                \"Click and drag to edit value.\\n\"\n                \"Hold SHIFT/ALT for faster/slower edit.\\n\"\n                \"Double-click or CTRL+click to input value.\");\n\n            ImGui::DragInt(\"drag int 0..100\", &i2, 1, 0, 100, \"%d%%\", ImGuiSliderFlags_AlwaysClamp);\n\n            static float f1 = 1.00f, f2 = 0.0067f;\n            ImGui::DragFloat(\"drag float\", &f1, 0.005f);\n            ImGui::DragFloat(\"drag small float\", &f2, 0.0001f, 0.0f, 0.0f, \"%.06f ns\");\n        }\n\n        ImGui::SeparatorText(\"Sliders\");\n\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/SliderInt, SliderFloat\");\n            static int i1 = 0;\n            ImGui::SliderInt(\"slider int\", &i1, -1, 3);\n            ImGui::SameLine(); HelpMarker(\"CTRL+click to input value.\");\n\n            static float f1 = 0.123f, f2 = 0.0f;\n            ImGui::SliderFloat(\"slider float\", &f1, 0.0f, 1.0f, \"ratio = %.3f\");\n            ImGui::SliderFloat(\"slider float (log)\", &f2, -10.0f, 10.0f, \"%.4f\", ImGuiSliderFlags_Logarithmic);\n\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/SliderAngle\");\n            static float angle = 0.0f;\n            ImGui::SliderAngle(\"slider angle\", &angle);\n\n            // Using the format string to display a name instead of an integer.\n            // Here we completely omit '%d' from the format string, so it'll only display a name.\n            // This technique can also be used with DragInt().\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/Slider (enum)\");\n            enum Element { Element_Fire, Element_Earth, Element_Air, Element_Water, Element_COUNT };\n            static int elem = Element_Fire;\n            const char* elems_names[Element_COUNT] = { \"Fire\", \"Earth\", \"Air\", \"Water\" };\n            const char* elem_name = (elem >= 0 && elem < Element_COUNT) ? elems_names[elem] : \"Unknown\";\n            ImGui::SliderInt(\"slider enum\", &elem, 0, Element_COUNT - 1, elem_name); // Use ImGuiSliderFlags_NoInput flag to disable CTRL+Click here.\n            ImGui::SameLine(); HelpMarker(\"Using the format string parameter to display a name instead of the underlying integer.\");\n        }\n\n        ImGui::SeparatorText(\"Selectors/Pickers\");\n\n        {\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/ColorEdit3, ColorEdit4\");\n            static float col1[3] = { 1.0f, 0.0f, 0.2f };\n            static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };\n            ImGui::ColorEdit3(\"color 1\", col1);\n            ImGui::SameLine(); HelpMarker(\n                \"Click on the color square to open a color picker.\\n\"\n                \"Click and hold to use drag and drop.\\n\"\n                \"Right-click on the color square to show options.\\n\"\n                \"CTRL+click on individual component to input value.\\n\");\n\n            ImGui::ColorEdit4(\"color 2\", col2);\n        }\n\n        {\n            // Using the _simplified_ one-liner Combo() api here\n            // See \"Combo\" section for examples of how to use the more flexible BeginCombo()/EndCombo() api.\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/Combo\");\n            const char* items[] = { \"AAAA\", \"BBBB\", \"CCCC\", \"DDDD\", \"EEEE\", \"FFFF\", \"GGGG\", \"HHHH\", \"IIIIIII\", \"JJJJ\", \"KKKKKKK\" };\n            static int item_current = 0;\n            ImGui::Combo(\"combo\", &item_current, items, IM_ARRAYSIZE(items));\n            ImGui::SameLine(); HelpMarker(\n                \"Using the simplified one-liner Combo API here.\\nRefer to the \\\"Combo\\\" section below for an explanation of how to use the more flexible and general BeginCombo/EndCombo API.\");\n        }\n\n        {\n            // Using the _simplified_ one-liner ListBox() api here\n            // See \"List boxes\" section for examples of how to use the more flexible BeginListBox()/EndListBox() api.\n            IMGUI_DEMO_MARKER(\"Widgets/Basic/ListBox\");\n            const char* items[] = { \"Apple\", \"Banana\", \"Cherry\", \"Kiwi\", \"Mango\", \"Orange\", \"Pineapple\", \"Strawberry\", \"Watermelon\" };\n            static int item_current = 1;\n            ImGui::ListBox(\"listbox\", &item_current, items, IM_ARRAYSIZE(items), 4);\n            ImGui::SameLine(); HelpMarker(\n                \"Using the simplified one-liner ListBox API here.\\nRefer to the \\\"List boxes\\\" section below for an explanation of how to use the more flexible and general BeginListBox/EndListBox API.\");\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Tooltips\");\n    if (ImGui::TreeNode(\"Tooltips\"))\n    {\n        // Tooltips are windows following the mouse. They do not take focus away.\n        ImGui::SeparatorText(\"General\");\n\n        // Typical use cases:\n        // - Short-form (text only):      SetItemTooltip(\"Hello\");\n        // - Short-form (any contents):   if (BeginItemTooltip()) { Text(\"Hello\"); EndTooltip(); }\n\n        // - Full-form (text only):       if (IsItemHovered(...)) { SetTooltip(\"Hello\"); }\n        // - Full-form (any contents):    if (IsItemHovered(...) && BeginTooltip()) { Text(\"Hello\"); EndTooltip(); }\n\n        HelpMarker(\n            \"Tooltip are typically created by using a IsItemHovered() + SetTooltip() sequence.\\n\\n\"\n            \"We provide a helper SetItemTooltip() function to perform the two with standards flags.\");\n\n        ImVec2 sz = ImVec2(-FLT_MIN, 0.0f);\n\n        ImGui::Button(\"Basic\", sz);\n        ImGui::SetItemTooltip(\"I am a tooltip\");\n\n        ImGui::Button(\"Fancy\", sz);\n        if (ImGui::BeginItemTooltip())\n        {\n            ImGui::Text(\"I am a fancy tooltip\");\n            static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };\n            ImGui::PlotLines(\"Curve\", arr, IM_ARRAYSIZE(arr));\n            ImGui::Text(\"Sin(time) = %f\", sinf((float)ImGui::GetTime()));\n            ImGui::EndTooltip();\n        }\n\n        ImGui::SeparatorText(\"Always On\");\n\n        // Showcase NOT relying on a IsItemHovered() to emit a tooltip.\n        // Here the tooltip is always emitted when 'always_on == true'.\n        static int always_on = 0;\n        ImGui::RadioButton(\"Off\", &always_on, 0);\n        ImGui::SameLine();\n        ImGui::RadioButton(\"Always On (Simple)\", &always_on, 1);\n        ImGui::SameLine();\n        ImGui::RadioButton(\"Always On (Advanced)\", &always_on, 2);\n        if (always_on == 1)\n            ImGui::SetTooltip(\"I am following you around.\");\n        else if (always_on == 2 && ImGui::BeginTooltip())\n        {\n            ImGui::ProgressBar(sinf((float)ImGui::GetTime()) * 0.5f + 0.5f, ImVec2(ImGui::GetFontSize() * 25, 0.0f));\n            ImGui::EndTooltip();\n        }\n\n        ImGui::SeparatorText(\"Custom\");\n\n        HelpMarker(\n            \"Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() is the preferred way to standardize\"\n            \"tooltip activation details across your application. You may however decide to use custom\"\n            \"flags for a specific tooltip instance.\");\n\n        // The following examples are passed for documentation purpose but may not be useful to most users.\n        // Passing ImGuiHoveredFlags_ForTooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from\n        // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or gamepad/keyboard is being used.\n        // With default settings, ImGuiHoveredFlags_ForTooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary.\n        ImGui::Button(\"Manual\", sz);\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n            ImGui::SetTooltip(\"I am a manually emitted tooltip.\");\n\n        ImGui::Button(\"DelayNone\", sz);\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNone))\n            ImGui::SetTooltip(\"I am a tooltip with no delay.\");\n\n        ImGui::Button(\"DelayShort\", sz);\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay))\n            ImGui::SetTooltip(\"I am a tooltip with a short delay (%0.2f sec).\", ImGui::GetStyle().HoverDelayShort);\n\n        ImGui::Button(\"DelayLong\", sz);\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay))\n            ImGui::SetTooltip(\"I am a tooltip with a long delay (%0.2f sec).\", ImGui::GetStyle().HoverDelayNormal);\n\n        ImGui::Button(\"Stationary\", sz);\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary))\n            ImGui::SetTooltip(\"I am a tooltip requiring mouse to be stationary before activating.\");\n\n        // Using ImGuiHoveredFlags_ForTooltip will pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav',\n        // which default value include the ImGuiHoveredFlags_AllowWhenDisabled flag.\n        // As a result, Set\n        ImGui::BeginDisabled();\n        ImGui::Button(\"Disabled item\", sz);\n        ImGui::EndDisabled();\n        if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n            ImGui::SetTooltip(\"I am a a tooltip for a disabled item.\");\n\n        ImGui::TreePop();\n    }\n\n    // Testing ImGuiOnceUponAFrame helper.\n    //static ImGuiOnceUponAFrame once;\n    //for (int i = 0; i < 5; i++)\n    //    if (once)\n    //        ImGui::Text(\"This will be displayed only once.\");\n\n    IMGUI_DEMO_MARKER(\"Widgets/Tree Nodes\");\n    if (ImGui::TreeNode(\"Tree Nodes\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Tree Nodes/Basic trees\");\n        if (ImGui::TreeNode(\"Basic trees\"))\n        {\n            for (int i = 0; i < 5; i++)\n            {\n                // Use SetNextItemOpen() so set the default state of a node to be open. We could\n                // also use TreeNodeEx() with the ImGuiTreeNodeFlags_DefaultOpen flag to achieve the same thing!\n                if (i == 0)\n                    ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n\n                if (ImGui::TreeNode((void*)(intptr_t)i, \"Child %d\", i))\n                {\n                    ImGui::Text(\"blah blah\");\n                    ImGui::SameLine();\n                    if (ImGui::SmallButton(\"button\")) {}\n                    ImGui::TreePop();\n                }\n            }\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Tree Nodes/Advanced, with Selectable nodes\");\n        if (ImGui::TreeNode(\"Advanced, with Selectable nodes\"))\n        {\n            HelpMarker(\n                \"This is a more typical looking tree with selectable nodes.\\n\"\n                \"Click to select, CTRL+Click to toggle, click on arrows or double-click to open.\");\n            static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;\n            static bool align_label_with_current_x_position = false;\n            static bool test_drag_and_drop = false;\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_OpenOnArrow\",       &base_flags, ImGuiTreeNodeFlags_OpenOnArrow);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_OpenOnDoubleClick\", &base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanAvailWidth\",    &base_flags, ImGuiTreeNodeFlags_SpanAvailWidth); ImGui::SameLine(); HelpMarker(\"Extend hit area to all available width instead of allowing more items to be laid out after the node.\");\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanFullWidth\",     &base_flags, ImGuiTreeNodeFlags_SpanFullWidth);\n            ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanAllColumns\",    &base_flags, ImGuiTreeNodeFlags_SpanAllColumns); ImGui::SameLine(); HelpMarker(\"For use in Tables only.\");\n            ImGui::Checkbox(\"Align label with current X position\", &align_label_with_current_x_position);\n            ImGui::Checkbox(\"Test tree node as drag source\", &test_drag_and_drop);\n            ImGui::Text(\"Hello!\");\n            if (align_label_with_current_x_position)\n                ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());\n\n            // 'selection_mask' is dumb representation of what may be user-side selection state.\n            //  You may retain selection state inside or outside your objects in whatever format you see fit.\n            // 'node_clicked' is temporary storage of what node we have clicked to process selection at the end\n            /// of the loop. May be a pointer to your own node type, etc.\n            static int selection_mask = (1 << 2);\n            int node_clicked = -1;\n            for (int i = 0; i < 6; i++)\n            {\n                // Disable the default \"open on single-click behavior\" + set Selected flag according to our selection.\n                // To alter selection we use IsItemClicked() && !IsItemToggledOpen(), so clicking on an arrow doesn't alter selection.\n                ImGuiTreeNodeFlags node_flags = base_flags;\n                const bool is_selected = (selection_mask & (1 << i)) != 0;\n                if (is_selected)\n                    node_flags |= ImGuiTreeNodeFlags_Selected;\n                if (i < 3)\n                {\n                    // Items 0..2 are Tree Node\n                    bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, \"Selectable Node %d\", i);\n                    if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())\n                        node_clicked = i;\n                    if (test_drag_and_drop && ImGui::BeginDragDropSource())\n                    {\n                        ImGui::SetDragDropPayload(\"_TREENODE\", NULL, 0);\n                        ImGui::Text(\"This is a drag and drop source\");\n                        ImGui::EndDragDropSource();\n                    }\n                    if (node_open)\n                    {\n                        ImGui::BulletText(\"Blah blah\\nBlah Blah\");\n                        ImGui::TreePop();\n                    }\n                }\n                else\n                {\n                    // Items 3..5 are Tree Leaves\n                    // The only reason we use TreeNode at all is to allow selection of the leaf. Otherwise we can\n                    // use BulletText() or advance the cursor by GetTreeNodeToLabelSpacing() and call Text().\n                    node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet\n                    ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, \"Selectable Leaf %d\", i);\n                    if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen())\n                        node_clicked = i;\n                    if (test_drag_and_drop && ImGui::BeginDragDropSource())\n                    {\n                        ImGui::SetDragDropPayload(\"_TREENODE\", NULL, 0);\n                        ImGui::Text(\"This is a drag and drop source\");\n                        ImGui::EndDragDropSource();\n                    }\n                }\n            }\n            if (node_clicked != -1)\n            {\n                // Update selection state\n                // (process outside of tree loop to avoid visual inconsistencies during the clicking frame)\n                if (ImGui::GetIO().KeyCtrl)\n                    selection_mask ^= (1 << node_clicked);          // CTRL+click to toggle\n                else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, may want to preserve selection when clicking on item that is part of the selection\n                    selection_mask = (1 << node_clicked);           // Click to single-select\n            }\n            if (align_label_with_current_x_position)\n                ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());\n            ImGui::TreePop();\n        }\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Collapsing Headers\");\n    if (ImGui::TreeNode(\"Collapsing Headers\"))\n    {\n        static bool closable_group = true;\n        ImGui::Checkbox(\"Show 2nd header\", &closable_group);\n        if (ImGui::CollapsingHeader(\"Header\", ImGuiTreeNodeFlags_None))\n        {\n            ImGui::Text(\"IsItemHovered: %d\", ImGui::IsItemHovered());\n            for (int i = 0; i < 5; i++)\n                ImGui::Text(\"Some content %d\", i);\n        }\n        if (ImGui::CollapsingHeader(\"Header with a close button\", &closable_group))\n        {\n            ImGui::Text(\"IsItemHovered: %d\", ImGui::IsItemHovered());\n            for (int i = 0; i < 5; i++)\n                ImGui::Text(\"More content %d\", i);\n        }\n        /*\n        if (ImGui::CollapsingHeader(\"Header with a bullet\", ImGuiTreeNodeFlags_Bullet))\n            ImGui::Text(\"IsItemHovered: %d\", ImGui::IsItemHovered());\n        */\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Bullets\");\n    if (ImGui::TreeNode(\"Bullets\"))\n    {\n        ImGui::BulletText(\"Bullet point 1\");\n        ImGui::BulletText(\"Bullet point 2\\nOn multiple lines\");\n        if (ImGui::TreeNode(\"Tree node\"))\n        {\n            ImGui::BulletText(\"Another bullet point\");\n            ImGui::TreePop();\n        }\n        ImGui::Bullet(); ImGui::Text(\"Bullet point 3 (two calls)\");\n        ImGui::Bullet(); ImGui::SmallButton(\"Button\");\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Text\");\n    if (ImGui::TreeNode(\"Text\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Text/Colored Text\");\n        if (ImGui::TreeNode(\"Colorful Text\"))\n        {\n            // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility.\n            ImGui::TextColored(ImVec4(1.0f, 0.0f, 1.0f, 1.0f), \"Pink\");\n            ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), \"Yellow\");\n            ImGui::TextDisabled(\"Disabled\");\n            ImGui::SameLine(); HelpMarker(\"The TextDisabled color is stored in ImGuiStyle.\");\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Text/Word Wrapping\");\n        if (ImGui::TreeNode(\"Word Wrapping\"))\n        {\n            // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility.\n            ImGui::TextWrapped(\n                \"This text should automatically wrap on the edge of the window. The current implementation \"\n                \"for text wrapping follows simple rules suitable for English and possibly other languages.\");\n            ImGui::Spacing();\n\n            static float wrap_width = 200.0f;\n            ImGui::SliderFloat(\"Wrap width\", &wrap_width, -20, 600, \"%.0f\");\n\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n            for (int n = 0; n < 2; n++)\n            {\n                ImGui::Text(\"Test paragraph %d:\", n);\n                ImVec2 pos = ImGui::GetCursorScreenPos();\n                ImVec2 marker_min = ImVec2(pos.x + wrap_width, pos.y);\n                ImVec2 marker_max = ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight());\n                ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width);\n                if (n == 0)\n                    ImGui::Text(\"The lazy dog is a good dog. This paragraph should fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.\", wrap_width);\n                else\n                    ImGui::Text(\"aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee   ffffffff. gggggggg!hhhhhhhh\");\n\n                // Draw actual text bounding box, following by marker of our expected limit (should not overlap!)\n                draw_list->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255, 255, 0, 255));\n                draw_list->AddRectFilled(marker_min, marker_max, IM_COL32(255, 0, 255, 255));\n                ImGui::PopTextWrapPos();\n            }\n\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Text/UTF-8 Text\");\n        if (ImGui::TreeNode(\"UTF-8 Text\"))\n        {\n            // UTF-8 test with Japanese characters\n            // (Needs a suitable font? Try \"Google Noto\" or \"Arial Unicode\". See docs/FONTS.md for details.)\n            // - From C++11 you can use the u8\"my text\" syntax to encode literal strings as UTF-8\n            // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. in Visual Studio, you\n            //   can save your source files as 'UTF-8 without signature').\n            // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8\n            //   CHARACTERS IN THIS SOURCE FILE. Instead we are encoding a few strings with hexadecimal constants.\n            //   Don't do this in your application! Please use u8\"text in any language\" in your application!\n            // Note that characters values are preserved even by InputText() if the font cannot be displayed,\n            // so you can safely copy & paste garbled characters into another application.\n            ImGui::TextWrapped(\n                \"CJK text will only appear if the font was loaded with the appropriate CJK character ranges. \"\n                \"Call io.Fonts->AddFontFromFileTTF() manually to load extra character ranges. \"\n                \"Read docs/FONTS.md for details.\");\n            ImGui::Text(\"Hiragana: \\xe3\\x81\\x8b\\xe3\\x81\\x8d\\xe3\\x81\\x8f\\xe3\\x81\\x91\\xe3\\x81\\x93 (kakikukeko)\"); // Normally we would use u8\"blah blah\" with the proper characters directly in the string.\n            ImGui::Text(\"Kanjis: \\xe6\\x97\\xa5\\xe6\\x9c\\xac\\xe8\\xaa\\x9e (nihongo)\");\n            static char buf[32] = \"\\xe6\\x97\\xa5\\xe6\\x9c\\xac\\xe8\\xaa\\x9e\";\n            //static char buf[32] = u8\"NIHONGO\"; // <- this is how you would write it with C++11, using real kanjis\n            ImGui::InputText(\"UTF-8 input\", buf, IM_ARRAYSIZE(buf));\n            ImGui::TreePop();\n        }\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Images\");\n    if (ImGui::TreeNode(\"Images\"))\n    {\n        ImGuiIO& io = ImGui::GetIO();\n        ImGui::TextWrapped(\n            \"Below we are displaying the font texture (which is the only texture we have access to in this demo). \"\n            \"Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. \"\n            \"Hover the texture for a zoomed view!\");\n\n        // Below we are displaying the font texture because it is the only texture we have access to inside the demo!\n        // Remember that ImTextureID is just storage for whatever you want it to be. It is essentially a value that\n        // will be passed to the rendering backend via the ImDrawCmd structure.\n        // If you use one of the default imgui_impl_XXXX.cpp rendering backend, they all have comments at the top\n        // of their respective source file to specify what they expect to be stored in ImTextureID, for example:\n        // - The imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer\n        // - The imgui_impl_opengl3.cpp renderer expect a GLuint OpenGL texture identifier, etc.\n        // More:\n        // - If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers\n        //   to ImGui::Image(), and gather width/height through your own functions, etc.\n        // - You can use ShowMetricsWindow() to inspect the draw data that are being passed to your renderer,\n        //   it will help you debug issues if you are confused about it.\n        // - Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage().\n        // - Read https://github.com/ocornut/imgui/blob/master/docs/FAQ.md\n        // - Read https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples\n        ImTextureID my_tex_id = io.Fonts->TexID;\n        float my_tex_w = (float)io.Fonts->TexWidth;\n        float my_tex_h = (float)io.Fonts->TexHeight;\n        {\n            static bool use_text_color_for_tint = false;\n            ImGui::Checkbox(\"Use Text Color for Tint\", &use_text_color_for_tint);\n            ImGui::Text(\"%.0fx%.0f\", my_tex_w, my_tex_h);\n            ImVec2 pos = ImGui::GetCursorScreenPos();\n            ImVec2 uv_min = ImVec2(0.0f, 0.0f);                 // Top-left\n            ImVec2 uv_max = ImVec2(1.0f, 1.0f);                 // Lower-right\n            ImVec4 tint_col = use_text_color_for_tint ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f);   // No tint\n            ImVec4 border_col = ImGui::GetStyleColorVec4(ImGuiCol_Border);\n            ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, tint_col, border_col);\n            if (ImGui::BeginItemTooltip())\n            {\n                float region_sz = 32.0f;\n                float region_x = io.MousePos.x - pos.x - region_sz * 0.5f;\n                float region_y = io.MousePos.y - pos.y - region_sz * 0.5f;\n                float zoom = 4.0f;\n                if (region_x < 0.0f) { region_x = 0.0f; }\n                else if (region_x > my_tex_w - region_sz) { region_x = my_tex_w - region_sz; }\n                if (region_y < 0.0f) { region_y = 0.0f; }\n                else if (region_y > my_tex_h - region_sz) { region_y = my_tex_h - region_sz; }\n                ImGui::Text(\"Min: (%.2f, %.2f)\", region_x, region_y);\n                ImGui::Text(\"Max: (%.2f, %.2f)\", region_x + region_sz, region_y + region_sz);\n                ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h);\n                ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h);\n                ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, tint_col, border_col);\n                ImGui::EndTooltip();\n            }\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Images/Textured buttons\");\n        ImGui::TextWrapped(\"And now some textured buttons..\");\n        static int pressed_count = 0;\n        for (int i = 0; i < 8; i++)\n        {\n            // UV coordinates are often (0.0f, 0.0f) and (1.0f, 1.0f) to display an entire textures.\n            // Here are trying to display only a 32x32 pixels area of the texture, hence the UV computation.\n            // Read about UV coordinates here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples\n            ImGui::PushID(i);\n            if (i > 0)\n                ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(i - 1.0f, i - 1.0f));\n            ImVec2 size = ImVec2(32.0f, 32.0f);                         // Size of the image we want to make visible\n            ImVec2 uv0 = ImVec2(0.0f, 0.0f);                            // UV coordinates for lower-left\n            ImVec2 uv1 = ImVec2(32.0f / my_tex_w, 32.0f / my_tex_h);    // UV coordinates for (32,32) in our texture\n            ImVec4 bg_col = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);             // Black background\n            ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);           // No tint\n            if (ImGui::ImageButton(\"\", my_tex_id, size, uv0, uv1, bg_col, tint_col))\n                pressed_count += 1;\n            if (i > 0)\n                ImGui::PopStyleVar();\n            ImGui::PopID();\n            ImGui::SameLine();\n        }\n        ImGui::NewLine();\n        ImGui::Text(\"Pressed %d times.\", pressed_count);\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Combo\");\n    if (ImGui::TreeNode(\"Combo\"))\n    {\n        // Combo Boxes are also called \"Dropdown\" in other systems\n        // Expose flags as checkbox for the demo\n        static ImGuiComboFlags flags = 0;\n        ImGui::CheckboxFlags(\"ImGuiComboFlags_PopupAlignLeft\", &flags, ImGuiComboFlags_PopupAlignLeft);\n        ImGui::SameLine(); HelpMarker(\"Only makes a difference if the popup is larger than the combo\");\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_NoArrowButton\", &flags, ImGuiComboFlags_NoArrowButton))\n            flags &= ~ImGuiComboFlags_NoPreview;     // Clear the other flag, as we cannot combine both\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_NoPreview\", &flags, ImGuiComboFlags_NoPreview))\n            flags &= ~(ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_WidthFitPreview); // Clear the other flag, as we cannot combine both\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_WidthFitPreview\", &flags, ImGuiComboFlags_WidthFitPreview))\n            flags &= ~ImGuiComboFlags_NoPreview;\n\n        // Override default popup height\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_HeightSmall\", &flags, ImGuiComboFlags_HeightSmall))\n            flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightSmall);\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_HeightRegular\", &flags, ImGuiComboFlags_HeightRegular))\n            flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightRegular);\n        if (ImGui::CheckboxFlags(\"ImGuiComboFlags_HeightLargest\", &flags, ImGuiComboFlags_HeightLargest))\n            flags &= ~(ImGuiComboFlags_HeightMask_ & ~ImGuiComboFlags_HeightLargest);\n\n        // Using the generic BeginCombo() API, you have full control over how to display the combo contents.\n        // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively\n        // stored in the object itself, etc.)\n        const char* items[] = { \"AAAA\", \"BBBB\", \"CCCC\", \"DDDD\", \"EEEE\", \"FFFF\", \"GGGG\", \"HHHH\", \"IIII\", \"JJJJ\", \"KKKK\", \"LLLLLLL\", \"MMMM\", \"OOOOOOO\" };\n        static int item_current_idx = 0; // Here we store our selection data as an index.\n        const char* combo_preview_value = items[item_current_idx];  // Pass in the preview value visible before opening the combo (it could be anything)\n        if (ImGui::BeginCombo(\"combo 1\", combo_preview_value, flags))\n        {\n            for (int n = 0; n < IM_ARRAYSIZE(items); n++)\n            {\n                const bool is_selected = (item_current_idx == n);\n                if (ImGui::Selectable(items[n], is_selected))\n                    item_current_idx = n;\n\n                // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)\n                if (is_selected)\n                    ImGui::SetItemDefaultFocus();\n            }\n            ImGui::EndCombo();\n        }\n\n        ImGui::Spacing();\n        ImGui::SeparatorText(\"One-liner variants\");\n        HelpMarker(\"Flags above don't apply to this section.\");\n\n        // Simplified one-liner Combo() API, using values packed in a single constant string\n        // This is a convenience for when the selection set is small and known at compile-time.\n        static int item_current_2 = 0;\n        ImGui::Combo(\"combo 2 (one-liner)\", &item_current_2, \"aaaa\\0bbbb\\0cccc\\0dddd\\0eeee\\0\\0\");\n\n        // Simplified one-liner Combo() using an array of const char*\n        // This is not very useful (may obsolete): prefer using BeginCombo()/EndCombo() for full control.\n        static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview\n        ImGui::Combo(\"combo 3 (array)\", &item_current_3, items, IM_ARRAYSIZE(items));\n\n        // Simplified one-liner Combo() using an accessor function\n        static int item_current_4 = 0;\n        ImGui::Combo(\"combo 4 (function)\", &item_current_4, [](void* data, int n) { return ((const char**)data)[n]; }, items, IM_ARRAYSIZE(items));\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/List Boxes\");\n    if (ImGui::TreeNode(\"List boxes\"))\n    {\n        // BeginListBox() is essentially a thin wrapper to using BeginChild()/EndChild() with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.\n        // You may be tempted to simply use BeginChild() directly, however note that BeginChild() requires EndChild() to always be called (inconsistent with BeginListBox()/EndListBox()).\n\n        // Using the generic BeginListBox() API, you have full control over how to display the combo contents.\n        // (your selection data could be an index, a pointer to the object, an id for the object, a flag intrusively\n        // stored in the object itself, etc.)\n        const char* items[] = { \"AAAA\", \"BBBB\", \"CCCC\", \"DDDD\", \"EEEE\", \"FFFF\", \"GGGG\", \"HHHH\", \"IIII\", \"JJJJ\", \"KKKK\", \"LLLLLLL\", \"MMMM\", \"OOOOOOO\" };\n        static int item_current_idx = 0; // Here we store our selection data as an index.\n        if (ImGui::BeginListBox(\"listbox 1\"))\n        {\n            for (int n = 0; n < IM_ARRAYSIZE(items); n++)\n            {\n                const bool is_selected = (item_current_idx == n);\n                if (ImGui::Selectable(items[n], is_selected))\n                    item_current_idx = n;\n\n                // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)\n                if (is_selected)\n                    ImGui::SetItemDefaultFocus();\n            }\n            ImGui::EndListBox();\n        }\n\n        // Custom size: use all width, 5 items tall\n        ImGui::Text(\"Full-width:\");\n        if (ImGui::BeginListBox(\"##listbox 2\", ImVec2(-FLT_MIN, 5 * ImGui::GetTextLineHeightWithSpacing())))\n        {\n            for (int n = 0; n < IM_ARRAYSIZE(items); n++)\n            {\n                const bool is_selected = (item_current_idx == n);\n                if (ImGui::Selectable(items[n], is_selected))\n                    item_current_idx = n;\n\n                // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)\n                if (is_selected)\n                    ImGui::SetItemDefaultFocus();\n            }\n            ImGui::EndListBox();\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Selectables\");\n    if (ImGui::TreeNode(\"Selectables\"))\n    {\n        // Selectable() has 2 overloads:\n        // - The one taking \"bool selected\" as a read-only selection information.\n        //   When Selectable() has been clicked it returns true and you can alter selection state accordingly.\n        // - The one taking \"bool* p_selected\" as a read-write selection information (convenient in some cases)\n        // The earlier is more flexible, as in real application your selection may be stored in many different ways\n        // and not necessarily inside a bool value (e.g. in flags within objects, as an external list, etc).\n        IMGUI_DEMO_MARKER(\"Widgets/Selectables/Basic\");\n        if (ImGui::TreeNode(\"Basic\"))\n        {\n            static bool selection[5] = { false, true, false, false };\n            ImGui::Selectable(\"1. I am selectable\", &selection[0]);\n            ImGui::Selectable(\"2. I am selectable\", &selection[1]);\n            ImGui::Selectable(\"3. I am selectable\", &selection[2]);\n            if (ImGui::Selectable(\"4. I am double clickable\", selection[3], ImGuiSelectableFlags_AllowDoubleClick))\n                if (ImGui::IsMouseDoubleClicked(0))\n                    selection[3] = !selection[3];\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Selectables/Single Selection\");\n        if (ImGui::TreeNode(\"Selection State: Single Selection\"))\n        {\n            static int selected = -1;\n            for (int n = 0; n < 5; n++)\n            {\n                char buf[32];\n                sprintf(buf, \"Object %d\", n);\n                if (ImGui::Selectable(buf, selected == n))\n                    selected = n;\n            }\n            ImGui::TreePop();\n        }\n        IMGUI_DEMO_MARKER(\"Widgets/Selectables/Multiple Selection\");\n        if (ImGui::TreeNode(\"Selection State: Multiple Selection\"))\n        {\n            HelpMarker(\"Hold CTRL and click to select multiple items.\");\n            static bool selection[5] = { false, false, false, false, false };\n            for (int n = 0; n < 5; n++)\n            {\n                char buf[32];\n                sprintf(buf, \"Object %d\", n);\n                if (ImGui::Selectable(buf, selection[n]))\n                {\n                    if (!ImGui::GetIO().KeyCtrl)    // Clear selection when CTRL is not held\n                        memset(selection, 0, sizeof(selection));\n                    selection[n] ^= 1;\n                }\n            }\n            ImGui::TreePop();\n        }\n        IMGUI_DEMO_MARKER(\"Widgets/Selectables/Rendering more items on the same line\");\n        if (ImGui::TreeNode(\"Rendering more items on the same line\"))\n        {\n            // (1) Using SetNextItemAllowOverlap()\n            // (2) Using the Selectable() override that takes \"bool* p_selected\" parameter, the bool value is toggled automatically.\n            static bool selected[3] = { false, false, false };\n            ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(\"main.c\",    &selected[0]); ImGui::SameLine(); ImGui::SmallButton(\"Link 1\");\n            ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(\"Hello.cpp\", &selected[1]); ImGui::SameLine(); ImGui::SmallButton(\"Link 2\");\n            ImGui::SetNextItemAllowOverlap(); ImGui::Selectable(\"Hello.h\",   &selected[2]); ImGui::SameLine(); ImGui::SmallButton(\"Link 3\");\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Selectables/In columns\");\n        if (ImGui::TreeNode(\"In columns\"))\n        {\n            static bool selected[10] = {};\n\n            if (ImGui::BeginTable(\"split1\", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))\n            {\n                for (int i = 0; i < 10; i++)\n                {\n                    char label[32];\n                    sprintf(label, \"Item %d\", i);\n                    ImGui::TableNextColumn();\n                    ImGui::Selectable(label, &selected[i]); // FIXME-TABLE: Selection overlap\n                }\n                ImGui::EndTable();\n            }\n            ImGui::Spacing();\n            if (ImGui::BeginTable(\"split2\", 3, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders))\n            {\n                for (int i = 0; i < 10; i++)\n                {\n                    char label[32];\n                    sprintf(label, \"Item %d\", i);\n                    ImGui::TableNextRow();\n                    ImGui::TableNextColumn();\n                    ImGui::Selectable(label, &selected[i], ImGuiSelectableFlags_SpanAllColumns);\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"Some other contents\");\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"123456\");\n                }\n                ImGui::EndTable();\n            }\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Selectables/Grid\");\n        if (ImGui::TreeNode(\"Grid\"))\n        {\n            static char selected[4][4] = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };\n\n            // Add in a bit of silly fun...\n            const float time = (float)ImGui::GetTime();\n            const bool winning_state = memchr(selected, 0, sizeof(selected)) == NULL; // If all cells are selected...\n            if (winning_state)\n                ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f + 0.5f * cosf(time * 2.0f), 0.5f + 0.5f * sinf(time * 3.0f)));\n\n            for (int y = 0; y < 4; y++)\n                for (int x = 0; x < 4; x++)\n                {\n                    if (x > 0)\n                        ImGui::SameLine();\n                    ImGui::PushID(y * 4 + x);\n                    if (ImGui::Selectable(\"Sailor\", selected[y][x] != 0, 0, ImVec2(50, 50)))\n                    {\n                        // Toggle clicked cell + toggle neighbors\n                        selected[y][x] ^= 1;\n                        if (x > 0) { selected[y][x - 1] ^= 1; }\n                        if (x < 3) { selected[y][x + 1] ^= 1; }\n                        if (y > 0) { selected[y - 1][x] ^= 1; }\n                        if (y < 3) { selected[y + 1][x] ^= 1; }\n                    }\n                    ImGui::PopID();\n                }\n\n            if (winning_state)\n                ImGui::PopStyleVar();\n            ImGui::TreePop();\n        }\n        IMGUI_DEMO_MARKER(\"Widgets/Selectables/Alignment\");\n        if (ImGui::TreeNode(\"Alignment\"))\n        {\n            HelpMarker(\n                \"By default, Selectables uses style.SelectableTextAlign but it can be overridden on a per-item \"\n                \"basis using PushStyleVar(). You'll probably want to always keep your default situation to \"\n                \"left-align otherwise it becomes difficult to layout multiple items on a same line\");\n            static bool selected[3 * 3] = { true, false, true, false, true, false, true, false, true };\n            for (int y = 0; y < 3; y++)\n            {\n                for (int x = 0; x < 3; x++)\n                {\n                    ImVec2 alignment = ImVec2((float)x / 2.0f, (float)y / 2.0f);\n                    char name[32];\n                    sprintf(name, \"(%.1f,%.1f)\", alignment.x, alignment.y);\n                    if (x > 0) ImGui::SameLine();\n                    ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, alignment);\n                    ImGui::Selectable(name, &selected[3 * y + x], ImGuiSelectableFlags_None, ImVec2(80, 80));\n                    ImGui::PopStyleVar();\n                }\n            }\n            ImGui::TreePop();\n        }\n        ImGui::TreePop();\n    }\n\n    // To wire InputText() with std::string or any other custom string type,\n    // see the \"Text Input > Resize Callback\" section of this demo, and the misc/cpp/imgui_stdlib.h file.\n    IMGUI_DEMO_MARKER(\"Widgets/Text Input\");\n    if (ImGui::TreeNode(\"Text Input\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Text Input/Multi-line Text Input\");\n        if (ImGui::TreeNode(\"Multi-line Text Input\"))\n        {\n            // Note: we are using a fixed-sized buffer for simplicity here. See ImGuiInputTextFlags_CallbackResize\n            // and the code in misc/cpp/imgui_stdlib.h for how to setup InputText() for dynamically resizing strings.\n            static char text[1024 * 16] =\n                \"/*\\n\"\n                \" The Pentium F00F bug, shorthand for F0 0F C7 C8,\\n\"\n                \" the hexadecimal encoding of one offending instruction,\\n\"\n                \" more formally, the invalid operand with locked CMPXCHG8B\\n\"\n                \" instruction bug, is a design flaw in the majority of\\n\"\n                \" Intel Pentium, Pentium MMX, and Pentium OverDrive\\n\"\n                \" processors (all in the P5 microarchitecture).\\n\"\n                \"*/\\n\\n\"\n                \"label:\\n\"\n                \"\\tlock cmpxchg8b eax\\n\";\n\n            static ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;\n            HelpMarker(\"You can use the ImGuiInputTextFlags_CallbackResize facility if you need to wire InputTextMultiline() to a dynamic string type. See misc/cpp/imgui_stdlib.h for an example. (This is not demonstrated in imgui_demo.cpp because we don't want to include <string> in here)\");\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_ReadOnly\", &flags, ImGuiInputTextFlags_ReadOnly);\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_AllowTabInput\", &flags, ImGuiInputTextFlags_AllowTabInput);\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_CtrlEnterForNewLine\", &flags, ImGuiInputTextFlags_CtrlEnterForNewLine);\n            ImGui::InputTextMultiline(\"##source\", text, IM_ARRAYSIZE(text), ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16), flags);\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Text Input/Filtered Text Input\");\n        if (ImGui::TreeNode(\"Filtered Text Input\"))\n        {\n            struct TextFilters\n            {\n                // Modify character input by altering 'data->Eventchar' (ImGuiInputTextFlags_CallbackCharFilter callback)\n                static int FilterCasingSwap(ImGuiInputTextCallbackData* data)\n                {\n                    if (data->EventChar >= 'a' && data->EventChar <= 'z')       { data->EventChar -= 'a' - 'A'; } // Lowercase becomes uppercase\n                    else if (data->EventChar >= 'A' && data->EventChar <= 'Z')  { data->EventChar += 'a' - 'A'; } // Uppercase becomes lowercase\n                    return 0;\n                }\n\n                // Return 0 (pass) if the character is 'i' or 'm' or 'g' or 'u' or 'i', otherwise return 1 (filter out)\n                static int FilterImGuiLetters(ImGuiInputTextCallbackData* data)\n                {\n                    if (data->EventChar < 256 && strchr(\"imgui\", (char)data->EventChar))\n                        return 0;\n                    return 1;\n                }\n            };\n\n            static char buf1[32] = \"\"; ImGui::InputText(\"default\",     buf1, 32);\n            static char buf2[32] = \"\"; ImGui::InputText(\"decimal\",     buf2, 32, ImGuiInputTextFlags_CharsDecimal);\n            static char buf3[32] = \"\"; ImGui::InputText(\"hexadecimal\", buf3, 32, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);\n            static char buf4[32] = \"\"; ImGui::InputText(\"uppercase\",   buf4, 32, ImGuiInputTextFlags_CharsUppercase);\n            static char buf5[32] = \"\"; ImGui::InputText(\"no blank\",    buf5, 32, ImGuiInputTextFlags_CharsNoBlank);\n            static char buf6[32] = \"\"; ImGui::InputText(\"casing swap\", buf6, 32, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterCasingSwap); // Use CharFilter callback to replace characters.\n            static char buf7[32] = \"\"; ImGui::InputText(\"\\\"imgui\\\"\",   buf7, 32, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); // Use CharFilter callback to disable some characters.\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Text Input/Password input\");\n        if (ImGui::TreeNode(\"Password Input\"))\n        {\n            static char password[64] = \"password123\";\n            ImGui::InputText(\"password\", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password);\n            ImGui::SameLine(); HelpMarker(\"Display all characters as '*'.\\nDisable clipboard cut and copy.\\nDisable logging.\\n\");\n            ImGui::InputTextWithHint(\"password (w/ hint)\", \"<password>\", password, IM_ARRAYSIZE(password), ImGuiInputTextFlags_Password);\n            ImGui::InputText(\"password (clear)\", password, IM_ARRAYSIZE(password));\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Text Input/Completion, History, Edit Callbacks\");\n        if (ImGui::TreeNode(\"Completion, History, Edit Callbacks\"))\n        {\n            struct Funcs\n            {\n                static int MyCallback(ImGuiInputTextCallbackData* data)\n                {\n                    if (data->EventFlag == ImGuiInputTextFlags_CallbackCompletion)\n                    {\n                        data->InsertChars(data->CursorPos, \"..\");\n                    }\n                    else if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory)\n                    {\n                        if (data->EventKey == ImGuiKey_UpArrow)\n                        {\n                            data->DeleteChars(0, data->BufTextLen);\n                            data->InsertChars(0, \"Pressed Up!\");\n                            data->SelectAll();\n                        }\n                        else if (data->EventKey == ImGuiKey_DownArrow)\n                        {\n                            data->DeleteChars(0, data->BufTextLen);\n                            data->InsertChars(0, \"Pressed Down!\");\n                            data->SelectAll();\n                        }\n                    }\n                    else if (data->EventFlag == ImGuiInputTextFlags_CallbackEdit)\n                    {\n                        // Toggle casing of first character\n                        char c = data->Buf[0];\n                        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) data->Buf[0] ^= 32;\n                        data->BufDirty = true;\n\n                        // Increment a counter\n                        int* p_int = (int*)data->UserData;\n                        *p_int = *p_int + 1;\n                    }\n                    return 0;\n                }\n            };\n            static char buf1[64];\n            ImGui::InputText(\"Completion\", buf1, 64, ImGuiInputTextFlags_CallbackCompletion, Funcs::MyCallback);\n            ImGui::SameLine(); HelpMarker(\"Here we append \\\"..\\\" each time Tab is pressed. See 'Examples>Console' for a more meaningful demonstration of using this callback.\");\n\n            static char buf2[64];\n            ImGui::InputText(\"History\", buf2, 64, ImGuiInputTextFlags_CallbackHistory, Funcs::MyCallback);\n            ImGui::SameLine(); HelpMarker(\"Here we replace and select text each time Up/Down are pressed. See 'Examples>Console' for a more meaningful demonstration of using this callback.\");\n\n            static char buf3[64];\n            static int edit_count = 0;\n            ImGui::InputText(\"Edit\", buf3, 64, ImGuiInputTextFlags_CallbackEdit, Funcs::MyCallback, (void*)&edit_count);\n            ImGui::SameLine(); HelpMarker(\"Here we toggle the casing of the first character on every edit + count edits.\");\n            ImGui::SameLine(); ImGui::Text(\"(%d)\", edit_count);\n\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Text Input/Resize Callback\");\n        if (ImGui::TreeNode(\"Resize Callback\"))\n        {\n            // To wire InputText() with std::string or any other custom string type,\n            // you can use the ImGuiInputTextFlags_CallbackResize flag + create a custom ImGui::InputText() wrapper\n            // using your preferred type. See misc/cpp/imgui_stdlib.h for an implementation of this using std::string.\n            HelpMarker(\n                \"Using ImGuiInputTextFlags_CallbackResize to wire your custom string type to InputText().\\n\\n\"\n                \"See misc/cpp/imgui_stdlib.h for an implementation of this for std::string.\");\n            struct Funcs\n            {\n                static int MyResizeCallback(ImGuiInputTextCallbackData* data)\n                {\n                    if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)\n                    {\n                        ImVector<char>* my_str = (ImVector<char>*)data->UserData;\n                        IM_ASSERT(my_str->begin() == data->Buf);\n                        my_str->resize(data->BufSize); // NB: On resizing calls, generally data->BufSize == data->BufTextLen + 1\n                        data->Buf = my_str->begin();\n                    }\n                    return 0;\n                }\n\n                // Note: Because ImGui:: is a namespace you would typically add your own function into the namespace.\n                // For example, you code may declare a function 'ImGui::InputText(const char* label, MyString* my_str)'\n                static bool MyInputTextMultiline(const char* label, ImVector<char>* my_str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0)\n                {\n                    IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);\n                    return ImGui::InputTextMultiline(label, my_str->begin(), (size_t)my_str->size(), size, flags | ImGuiInputTextFlags_CallbackResize, Funcs::MyResizeCallback, (void*)my_str);\n                }\n            };\n\n            // For this demo we are using ImVector as a string container.\n            // Note that because we need to store a terminating zero character, our size/capacity are 1 more\n            // than usually reported by a typical string class.\n            static ImVector<char> my_str;\n            if (my_str.empty())\n                my_str.push_back(0);\n            Funcs::MyInputTextMultiline(\"##MyStr\", &my_str, ImVec2(-FLT_MIN, ImGui::GetTextLineHeight() * 16));\n            ImGui::Text(\"Data: %p\\nSize: %d\\nCapacity: %d\", (void*)my_str.begin(), my_str.size(), my_str.capacity());\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Text Input/Miscellaneous\");\n        if (ImGui::TreeNode(\"Miscellaneous\"))\n        {\n            static char buf1[16];\n            static ImGuiInputTextFlags flags = ImGuiInputTextFlags_EscapeClearsAll;\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_EscapeClearsAll\", &flags, ImGuiInputTextFlags_EscapeClearsAll);\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_ReadOnly\", &flags, ImGuiInputTextFlags_ReadOnly);\n            ImGui::CheckboxFlags(\"ImGuiInputTextFlags_NoUndoRedo\", &flags, ImGuiInputTextFlags_NoUndoRedo);\n            ImGui::InputText(\"Hello\", buf1, IM_ARRAYSIZE(buf1), flags);\n            ImGui::TreePop();\n        }\n\n        ImGui::TreePop();\n    }\n\n    // Tabs\n    IMGUI_DEMO_MARKER(\"Widgets/Tabs\");\n    if (ImGui::TreeNode(\"Tabs\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Tabs/Basic\");\n        if (ImGui::TreeNode(\"Basic\"))\n        {\n            ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None;\n            if (ImGui::BeginTabBar(\"MyTabBar\", tab_bar_flags))\n            {\n                if (ImGui::BeginTabItem(\"Avocado\"))\n                {\n                    ImGui::Text(\"This is the Avocado tab!\\nblah blah blah blah blah\");\n                    ImGui::EndTabItem();\n                }\n                if (ImGui::BeginTabItem(\"Broccoli\"))\n                {\n                    ImGui::Text(\"This is the Broccoli tab!\\nblah blah blah blah blah\");\n                    ImGui::EndTabItem();\n                }\n                if (ImGui::BeginTabItem(\"Cucumber\"))\n                {\n                    ImGui::Text(\"This is the Cucumber tab!\\nblah blah blah blah blah\");\n                    ImGui::EndTabItem();\n                }\n                ImGui::EndTabBar();\n            }\n            ImGui::Separator();\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Tabs/Advanced & Close Button\");\n        if (ImGui::TreeNode(\"Advanced & Close Button\"))\n        {\n            // Expose a couple of the available flags. In most cases you may just call BeginTabBar() with no flags (0).\n            static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable;\n            ImGui::CheckboxFlags(\"ImGuiTabBarFlags_Reorderable\", &tab_bar_flags, ImGuiTabBarFlags_Reorderable);\n            ImGui::CheckboxFlags(\"ImGuiTabBarFlags_AutoSelectNewTabs\", &tab_bar_flags, ImGuiTabBarFlags_AutoSelectNewTabs);\n            ImGui::CheckboxFlags(\"ImGuiTabBarFlags_TabListPopupButton\", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton);\n            ImGui::CheckboxFlags(\"ImGuiTabBarFlags_NoCloseWithMiddleMouseButton\", &tab_bar_flags, ImGuiTabBarFlags_NoCloseWithMiddleMouseButton);\n            if ((tab_bar_flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)\n                tab_bar_flags |= ImGuiTabBarFlags_FittingPolicyDefault_;\n            if (ImGui::CheckboxFlags(\"ImGuiTabBarFlags_FittingPolicyResizeDown\", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown))\n                tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown);\n            if (ImGui::CheckboxFlags(\"ImGuiTabBarFlags_FittingPolicyScroll\", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll))\n                tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);\n\n            // Tab Bar\n            const char* names[4] = { \"Artichoke\", \"Beetroot\", \"Celery\", \"Daikon\" };\n            static bool opened[4] = { true, true, true, true }; // Persistent user state\n            for (int n = 0; n < IM_ARRAYSIZE(opened); n++)\n            {\n                if (n > 0) { ImGui::SameLine(); }\n                ImGui::Checkbox(names[n], &opened[n]);\n            }\n\n            // Passing a bool* to BeginTabItem() is similar to passing one to Begin():\n            // the underlying bool will be set to false when the tab is closed.\n            if (ImGui::BeginTabBar(\"MyTabBar\", tab_bar_flags))\n            {\n                for (int n = 0; n < IM_ARRAYSIZE(opened); n++)\n                    if (opened[n] && ImGui::BeginTabItem(names[n], &opened[n], ImGuiTabItemFlags_None))\n                    {\n                        ImGui::Text(\"This is the %s tab!\", names[n]);\n                        if (n & 1)\n                            ImGui::Text(\"I am an odd tab.\");\n                        ImGui::EndTabItem();\n                    }\n                ImGui::EndTabBar();\n            }\n            ImGui::Separator();\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Tabs/TabItemButton & Leading-Trailing flags\");\n        if (ImGui::TreeNode(\"TabItemButton & Leading/Trailing flags\"))\n        {\n            static ImVector<int> active_tabs;\n            static int next_tab_id = 0;\n            if (next_tab_id == 0) // Initialize with some default tabs\n                for (int i = 0; i < 3; i++)\n                    active_tabs.push_back(next_tab_id++);\n\n            // TabItemButton() and Leading/Trailing flags are distinct features which we will demo together.\n            // (It is possible to submit regular tabs with Leading/Trailing flags, or TabItemButton tabs without Leading/Trailing flags...\n            // but they tend to make more sense together)\n            static bool show_leading_button = true;\n            static bool show_trailing_button = true;\n            ImGui::Checkbox(\"Show Leading TabItemButton()\", &show_leading_button);\n            ImGui::Checkbox(\"Show Trailing TabItemButton()\", &show_trailing_button);\n\n            // Expose some other flags which are useful to showcase how they interact with Leading/Trailing tabs\n            static ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyResizeDown;\n            ImGui::CheckboxFlags(\"ImGuiTabBarFlags_TabListPopupButton\", &tab_bar_flags, ImGuiTabBarFlags_TabListPopupButton);\n            if (ImGui::CheckboxFlags(\"ImGuiTabBarFlags_FittingPolicyResizeDown\", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyResizeDown))\n                tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyResizeDown);\n            if (ImGui::CheckboxFlags(\"ImGuiTabBarFlags_FittingPolicyScroll\", &tab_bar_flags, ImGuiTabBarFlags_FittingPolicyScroll))\n                tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ ImGuiTabBarFlags_FittingPolicyScroll);\n\n            if (ImGui::BeginTabBar(\"MyTabBar\", tab_bar_flags))\n            {\n                // Demo a Leading TabItemButton(): click the \"?\" button to open a menu\n                if (show_leading_button)\n                    if (ImGui::TabItemButton(\"?\", ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_NoTooltip))\n                        ImGui::OpenPopup(\"MyHelpMenu\");\n                if (ImGui::BeginPopup(\"MyHelpMenu\"))\n                {\n                    ImGui::Selectable(\"Hello!\");\n                    ImGui::EndPopup();\n                }\n\n                // Demo Trailing Tabs: click the \"+\" button to add a new tab (in your app you may want to use a font icon instead of the \"+\")\n                // Note that we submit it before the regular tabs, but because of the ImGuiTabItemFlags_Trailing flag it will always appear at the end.\n                if (show_trailing_button)\n                    if (ImGui::TabItemButton(\"+\", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip))\n                        active_tabs.push_back(next_tab_id++); // Add new tab\n\n                // Submit our regular tabs\n                for (int n = 0; n < active_tabs.Size; )\n                {\n                    bool open = true;\n                    char name[16];\n                    snprintf(name, IM_ARRAYSIZE(name), \"%04d\", active_tabs[n]);\n                    if (ImGui::BeginTabItem(name, &open, ImGuiTabItemFlags_None))\n                    {\n                        ImGui::Text(\"This is the %s tab!\", name);\n                        ImGui::EndTabItem();\n                    }\n\n                    if (!open)\n                        active_tabs.erase(active_tabs.Data + n);\n                    else\n                        n++;\n                }\n\n                ImGui::EndTabBar();\n            }\n            ImGui::Separator();\n            ImGui::TreePop();\n        }\n        ImGui::TreePop();\n    }\n\n    // Plot/Graph widgets are not very good.\n    // Consider using a third-party library such as ImPlot: https://github.com/epezent/implot\n    // (see others https://github.com/ocornut/imgui/wiki/Useful-Extensions)\n    IMGUI_DEMO_MARKER(\"Widgets/Plotting\");\n    if (ImGui::TreeNode(\"Plotting\"))\n    {\n        static bool animate = true;\n        ImGui::Checkbox(\"Animate\", &animate);\n\n        // Plot as lines and plot as histogram\n        IMGUI_DEMO_MARKER(\"Widgets/Plotting/PlotLines, PlotHistogram\");\n        static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f };\n        ImGui::PlotLines(\"Frame Times\", arr, IM_ARRAYSIZE(arr));\n        ImGui::PlotHistogram(\"Histogram\", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0, 80.0f));\n\n        // Fill an array of contiguous float values to plot\n        // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float\n        // and the sizeof() of your structure in the \"stride\" parameter.\n        static float values[90] = {};\n        static int values_offset = 0;\n        static double refresh_time = 0.0;\n        if (!animate || refresh_time == 0.0)\n            refresh_time = ImGui::GetTime();\n        while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 Hz rate for the demo\n        {\n            static float phase = 0.0f;\n            values[values_offset] = cosf(phase);\n            values_offset = (values_offset + 1) % IM_ARRAYSIZE(values);\n            phase += 0.10f * values_offset;\n            refresh_time += 1.0f / 60.0f;\n        }\n\n        // Plots can display overlay texts\n        // (in this example, we will display an average value)\n        {\n            float average = 0.0f;\n            for (int n = 0; n < IM_ARRAYSIZE(values); n++)\n                average += values[n];\n            average /= (float)IM_ARRAYSIZE(values);\n            char overlay[32];\n            sprintf(overlay, \"avg %f\", average);\n            ImGui::PlotLines(\"Lines\", values, IM_ARRAYSIZE(values), values_offset, overlay, -1.0f, 1.0f, ImVec2(0, 80.0f));\n        }\n\n        // Use functions to generate output\n        // FIXME: This is actually VERY awkward because current plot API only pass in indices.\n        // We probably want an API passing floats and user provide sample rate/count.\n        struct Funcs\n        {\n            static float Sin(void*, int i) { return sinf(i * 0.1f); }\n            static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; }\n        };\n        static int func_type = 0, display_count = 70;\n        ImGui::SeparatorText(\"Functions\");\n        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n        ImGui::Combo(\"func\", &func_type, \"Sin\\0Saw\\0\");\n        ImGui::SameLine();\n        ImGui::SliderInt(\"Sample count\", &display_count, 1, 400);\n        float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw;\n        ImGui::PlotLines(\"Lines\", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));\n        ImGui::PlotHistogram(\"Histogram\", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0, 80));\n        ImGui::Separator();\n\n        // Animate a simple progress bar\n        IMGUI_DEMO_MARKER(\"Widgets/Plotting/ProgressBar\");\n        static float progress = 0.0f, progress_dir = 1.0f;\n        if (animate)\n        {\n            progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime;\n            if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; }\n            if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; }\n        }\n\n        // Typically we would use ImVec2(-1.0f,0.0f) or ImVec2(-FLT_MIN,0.0f) to use all available width,\n        // or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth.\n        ImGui::ProgressBar(progress, ImVec2(0.0f, 0.0f));\n        ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);\n        ImGui::Text(\"Progress Bar\");\n\n        float progress_saturated = IM_CLAMP(progress, 0.0f, 1.0f);\n        char buf[32];\n        sprintf(buf, \"%d/%d\", (int)(progress_saturated * 1753), 1753);\n        ImGui::ProgressBar(progress, ImVec2(0.f, 0.f), buf);\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Color\");\n    if (ImGui::TreeNode(\"Color/Picker Widgets\"))\n    {\n        static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f);\n\n        static bool alpha_preview = true;\n        static bool alpha_half_preview = false;\n        static bool drag_and_drop = true;\n        static bool options_menu = true;\n        static bool hdr = false;\n        ImGui::SeparatorText(\"Options\");\n        ImGui::Checkbox(\"With Alpha Preview\", &alpha_preview);\n        ImGui::Checkbox(\"With Half Alpha Preview\", &alpha_half_preview);\n        ImGui::Checkbox(\"With Drag and Drop\", &drag_and_drop);\n        ImGui::Checkbox(\"With Options Menu\", &options_menu); ImGui::SameLine(); HelpMarker(\"Right-click on the individual color widget to show options.\");\n        ImGui::Checkbox(\"With HDR\", &hdr); ImGui::SameLine(); HelpMarker(\"Currently all this does is to lift the 0..1 limits on dragging widgets.\");\n        ImGuiColorEditFlags misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorEdit\");\n        ImGui::SeparatorText(\"Inline color editor\");\n        ImGui::Text(\"Color widget:\");\n        ImGui::SameLine(); HelpMarker(\n            \"Click on the color square to open a color picker.\\n\"\n            \"CTRL+click on individual component to input value.\\n\");\n        ImGui::ColorEdit3(\"MyColor##1\", (float*)&color, misc_flags);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorEdit (HSV, with Alpha)\");\n        ImGui::Text(\"Color widget HSV with Alpha:\");\n        ImGui::ColorEdit4(\"MyColor##2\", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorEdit (float display)\");\n        ImGui::Text(\"Color widget with Float Display:\");\n        ImGui::ColorEdit4(\"MyColor##2f\", (float*)&color, ImGuiColorEditFlags_Float | misc_flags);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorButton (with Picker)\");\n        ImGui::Text(\"Color button with Picker:\");\n        ImGui::SameLine(); HelpMarker(\n            \"With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\\n\"\n            \"With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only \"\n            \"be used for the tooltip and picker popup.\");\n        ImGui::ColorEdit4(\"MyColor##3\", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorButton (with custom Picker popup)\");\n        ImGui::Text(\"Color button with Custom Picker Popup:\");\n\n        // Generate a default palette. The palette will persist and can be edited.\n        static bool saved_palette_init = true;\n        static ImVec4 saved_palette[32] = {};\n        if (saved_palette_init)\n        {\n            for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)\n            {\n                ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f,\n                    saved_palette[n].x, saved_palette[n].y, saved_palette[n].z);\n                saved_palette[n].w = 1.0f; // Alpha\n            }\n            saved_palette_init = false;\n        }\n\n        static ImVec4 backup_color;\n        bool open_popup = ImGui::ColorButton(\"MyColor##3b\", color, misc_flags);\n        ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);\n        open_popup |= ImGui::Button(\"Palette\");\n        if (open_popup)\n        {\n            ImGui::OpenPopup(\"mypicker\");\n            backup_color = color;\n        }\n        if (ImGui::BeginPopup(\"mypicker\"))\n        {\n            ImGui::Text(\"MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!\");\n            ImGui::Separator();\n            ImGui::ColorPicker4(\"##picker\", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview);\n            ImGui::SameLine();\n\n            ImGui::BeginGroup(); // Lock X position\n            ImGui::Text(\"Current\");\n            ImGui::ColorButton(\"##current\", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40));\n            ImGui::Text(\"Previous\");\n            if (ImGui::ColorButton(\"##previous\", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60, 40)))\n                color = backup_color;\n            ImGui::Separator();\n            ImGui::Text(\"Palette\");\n            for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++)\n            {\n                ImGui::PushID(n);\n                if ((n % 8) != 0)\n                    ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y);\n\n                ImGuiColorEditFlags palette_button_flags = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip;\n                if (ImGui::ColorButton(\"##palette\", saved_palette[n], palette_button_flags, ImVec2(20, 20)))\n                    color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha!\n\n                // Allow user to drop colors into each palette entry. Note that ColorButton() is already a\n                // drag source by default, unless specifying the ImGuiColorEditFlags_NoDragDrop flag.\n                if (ImGui::BeginDragDropTarget())\n                {\n                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))\n                        memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3);\n                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))\n                        memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4);\n                    ImGui::EndDragDropTarget();\n                }\n\n                ImGui::PopID();\n            }\n            ImGui::EndGroup();\n            ImGui::EndPopup();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorButton (simple)\");\n        ImGui::Text(\"Color button only:\");\n        static bool no_border = false;\n        ImGui::Checkbox(\"ImGuiColorEditFlags_NoBorder\", &no_border);\n        ImGui::ColorButton(\"MyColor##3c\", *(ImVec4*)&color, misc_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80));\n\n        IMGUI_DEMO_MARKER(\"Widgets/Color/ColorPicker\");\n        ImGui::SeparatorText(\"Color picker\");\n        static bool alpha = true;\n        static bool alpha_bar = true;\n        static bool side_preview = true;\n        static bool ref_color = false;\n        static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f);\n        static int display_mode = 0;\n        static int picker_mode = 0;\n        ImGui::Checkbox(\"With Alpha\", &alpha);\n        ImGui::Checkbox(\"With Alpha Bar\", &alpha_bar);\n        ImGui::Checkbox(\"With Side Preview\", &side_preview);\n        if (side_preview)\n        {\n            ImGui::SameLine();\n            ImGui::Checkbox(\"With Ref Color\", &ref_color);\n            if (ref_color)\n            {\n                ImGui::SameLine();\n                ImGui::ColorEdit4(\"##RefColor\", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags);\n            }\n        }\n        ImGui::Combo(\"Display Mode\", &display_mode, \"Auto/Current\\0None\\0RGB Only\\0HSV Only\\0Hex Only\\0\");\n        ImGui::SameLine(); HelpMarker(\n            \"ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, \"\n            \"but the user can change it with a right-click on those inputs.\\n\\nColorPicker defaults to displaying RGB+HSV+Hex \"\n            \"if you don't specify a display mode.\\n\\nYou can change the defaults using SetColorEditOptions().\");\n        ImGui::SameLine(); HelpMarker(\"When not specified explicitly (Auto/Current mode), user can right-click the picker to change mode.\");\n        ImGuiColorEditFlags flags = misc_flags;\n        if (!alpha)            flags |= ImGuiColorEditFlags_NoAlpha;        // This is by default if you call ColorPicker3() instead of ColorPicker4()\n        if (alpha_bar)         flags |= ImGuiColorEditFlags_AlphaBar;\n        if (!side_preview)     flags |= ImGuiColorEditFlags_NoSidePreview;\n        if (picker_mode == 1)  flags |= ImGuiColorEditFlags_PickerHueBar;\n        if (picker_mode == 2)  flags |= ImGuiColorEditFlags_PickerHueWheel;\n        if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs;       // Disable all RGB/HSV/Hex displays\n        if (display_mode == 2) flags |= ImGuiColorEditFlags_DisplayRGB;     // Override display mode\n        if (display_mode == 3) flags |= ImGuiColorEditFlags_DisplayHSV;\n        if (display_mode == 4) flags |= ImGuiColorEditFlags_DisplayHex;\n        ImGui::ColorPicker4(\"MyColor##4\", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL);\n\n        ImGui::Text(\"Set defaults in code:\");\n        ImGui::SameLine(); HelpMarker(\n            \"SetColorEditOptions() is designed to allow you to set boot-time default.\\n\"\n            \"We don't have Push/Pop functions because you can force options on a per-widget basis if needed,\"\n            \"and the user can change non-forced ones with the options menu.\\nWe don't have a getter to avoid\"\n            \"encouraging you to persistently save values that aren't forward-compatible.\");\n        if (ImGui::Button(\"Default: Uint8 + HSV + Hue Bar\"))\n            ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_PickerHueBar);\n        if (ImGui::Button(\"Default: Float + HDR + Hue Wheel\"))\n            ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel);\n\n        // Always both a small version of both types of pickers (to make it more visible in the demo to people who are skimming quickly through it)\n        ImGui::Text(\"Both types:\");\n        float w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.y) * 0.40f;\n        ImGui::SetNextItemWidth(w);\n        ImGui::ColorPicker3(\"##MyColor##5\", (float*)&color, ImGuiColorEditFlags_PickerHueBar | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);\n        ImGui::SameLine();\n        ImGui::SetNextItemWidth(w);\n        ImGui::ColorPicker3(\"##MyColor##6\", (float*)&color, ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha);\n\n        // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0)\n        static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV!\n        ImGui::Spacing();\n        ImGui::Text(\"HSV encoded colors\");\n        ImGui::SameLine(); HelpMarker(\n            \"By default, colors are given to ColorEdit and ColorPicker in RGB, but ImGuiColorEditFlags_InputHSV\"\n            \"allows you to store colors as HSV and pass them to ColorEdit and ColorPicker as HSV. This comes with the\"\n            \"added benefit that you can manipulate hue values with the picker even when saturation or value are zero.\");\n        ImGui::Text(\"Color widget with InputHSV:\");\n        ImGui::ColorEdit4(\"HSV shown as RGB##1\", (float*)&color_hsv, ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);\n        ImGui::ColorEdit4(\"HSV shown as HSV##1\", (float*)&color_hsv, ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_InputHSV | ImGuiColorEditFlags_Float);\n        ImGui::DragFloat4(\"Raw HSV values\", (float*)&color_hsv, 0.01f, 0.0f, 1.0f);\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Drag and Slider Flags\");\n    if (ImGui::TreeNode(\"Drag/Slider Flags\"))\n    {\n        // Demonstrate using advanced flags for DragXXX and SliderXXX functions. Note that the flags are the same!\n        static ImGuiSliderFlags flags = ImGuiSliderFlags_None;\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_AlwaysClamp\", &flags, ImGuiSliderFlags_AlwaysClamp);\n        ImGui::SameLine(); HelpMarker(\"Always clamp value to min/max bounds (if any) when input manually with CTRL+Click.\");\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_Logarithmic\", &flags, ImGuiSliderFlags_Logarithmic);\n        ImGui::SameLine(); HelpMarker(\"Enable logarithmic editing (more precision for small values).\");\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_NoRoundToFormat\", &flags, ImGuiSliderFlags_NoRoundToFormat);\n        ImGui::SameLine(); HelpMarker(\"Disable rounding underlying value to match precision of the format string (e.g. %.3f values are rounded to those 3 digits).\");\n        ImGui::CheckboxFlags(\"ImGuiSliderFlags_NoInput\", &flags, ImGuiSliderFlags_NoInput);\n        ImGui::SameLine(); HelpMarker(\"Disable CTRL+Click or Enter key allowing to input text directly into the widget.\");\n\n        // Drags\n        static float drag_f = 0.5f;\n        static int drag_i = 50;\n        ImGui::Text(\"Underlying float value: %f\", drag_f);\n        ImGui::DragFloat(\"DragFloat (0 -> 1)\", &drag_f, 0.005f, 0.0f, 1.0f, \"%.3f\", flags);\n        ImGui::DragFloat(\"DragFloat (0 -> +inf)\", &drag_f, 0.005f, 0.0f, FLT_MAX, \"%.3f\", flags);\n        ImGui::DragFloat(\"DragFloat (-inf -> 1)\", &drag_f, 0.005f, -FLT_MAX, 1.0f, \"%.3f\", flags);\n        ImGui::DragFloat(\"DragFloat (-inf -> +inf)\", &drag_f, 0.005f, -FLT_MAX, +FLT_MAX, \"%.3f\", flags);\n        ImGui::DragInt(\"DragInt (0 -> 100)\", &drag_i, 0.5f, 0, 100, \"%d\", flags);\n\n        // Sliders\n        static float slider_f = 0.5f;\n        static int slider_i = 50;\n        ImGui::Text(\"Underlying float value: %f\", slider_f);\n        ImGui::SliderFloat(\"SliderFloat (0 -> 1)\", &slider_f, 0.0f, 1.0f, \"%.3f\", flags);\n        ImGui::SliderInt(\"SliderInt (0 -> 100)\", &slider_i, 0, 100, \"%d\", flags);\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Range Widgets\");\n    if (ImGui::TreeNode(\"Range Widgets\"))\n    {\n        static float begin = 10, end = 90;\n        static int begin_i = 100, end_i = 1000;\n        ImGui::DragFloatRange2(\"range float\", &begin, &end, 0.25f, 0.0f, 100.0f, \"Min: %.1f %%\", \"Max: %.1f %%\", ImGuiSliderFlags_AlwaysClamp);\n        ImGui::DragIntRange2(\"range int\", &begin_i, &end_i, 5, 0, 1000, \"Min: %d units\", \"Max: %d units\");\n        ImGui::DragIntRange2(\"range int (no bounds)\", &begin_i, &end_i, 5, 0, 0, \"Min: %d units\", \"Max: %d units\");\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Data Types\");\n    if (ImGui::TreeNode(\"Data Types\"))\n    {\n        // DragScalar/InputScalar/SliderScalar functions allow various data types\n        // - signed/unsigned\n        // - 8/16/32/64-bits\n        // - integer/float/double\n        // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum\n        // to pass the type, and passing all arguments by pointer.\n        // This is the reason the test code below creates local variables to hold \"zero\" \"one\" etc. for each type.\n        // In practice, if you frequently use a given type that is not covered by the normal API entry points,\n        // you can wrap it yourself inside a 1 line function which can take typed argument as value instead of void*,\n        // and then pass their address to the generic function. For example:\n        //   bool MySliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = \"%lld\")\n        //   {\n        //      return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format);\n        //   }\n\n        // Setup limits (as helper variables so we can take their address, as explained above)\n        // Note: SliderScalar() functions have a maximum usable range of half the natural type maximum, hence the /2.\n        #ifndef LLONG_MIN\n        ImS64 LLONG_MIN = -9223372036854775807LL - 1;\n        ImS64 LLONG_MAX = 9223372036854775807LL;\n        ImU64 ULLONG_MAX = (2ULL * 9223372036854775807LL + 1);\n        #endif\n        const char    s8_zero  = 0,   s8_one  = 1,   s8_fifty  = 50, s8_min  = -128,        s8_max = 127;\n        const ImU8    u8_zero  = 0,   u8_one  = 1,   u8_fifty  = 50, u8_min  = 0,           u8_max = 255;\n        const short   s16_zero = 0,   s16_one = 1,   s16_fifty = 50, s16_min = -32768,      s16_max = 32767;\n        const ImU16   u16_zero = 0,   u16_one = 1,   u16_fifty = 50, u16_min = 0,           u16_max = 65535;\n        const ImS32   s32_zero = 0,   s32_one = 1,   s32_fifty = 50, s32_min = INT_MIN/2,   s32_max = INT_MAX/2,    s32_hi_a = INT_MAX/2 - 100,    s32_hi_b = INT_MAX/2;\n        const ImU32   u32_zero = 0,   u32_one = 1,   u32_fifty = 50, u32_min = 0,           u32_max = UINT_MAX/2,   u32_hi_a = UINT_MAX/2 - 100,   u32_hi_b = UINT_MAX/2;\n        const ImS64   s64_zero = 0,   s64_one = 1,   s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2,  s64_hi_a = LLONG_MAX/2 - 100,  s64_hi_b = LLONG_MAX/2;\n        const ImU64   u64_zero = 0,   u64_one = 1,   u64_fifty = 50, u64_min = 0,           u64_max = ULLONG_MAX/2, u64_hi_a = ULLONG_MAX/2 - 100, u64_hi_b = ULLONG_MAX/2;\n        const float   f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f;\n        const double  f64_zero = 0.,  f64_one = 1.,  f64_lo_a = -1000000000000000.0, f64_hi_a = +1000000000000000.0;\n\n        // State\n        static char   s8_v  = 127;\n        static ImU8   u8_v  = 255;\n        static short  s16_v = 32767;\n        static ImU16  u16_v = 65535;\n        static ImS32  s32_v = -1;\n        static ImU32  u32_v = (ImU32)-1;\n        static ImS64  s64_v = -1;\n        static ImU64  u64_v = (ImU64)-1;\n        static float  f32_v = 0.123f;\n        static double f64_v = 90000.01234567890123456789;\n\n        const float drag_speed = 0.2f;\n        static bool drag_clamp = false;\n        IMGUI_DEMO_MARKER(\"Widgets/Data Types/Drags\");\n        ImGui::SeparatorText(\"Drags\");\n        ImGui::Checkbox(\"Clamp integers to 0..50\", &drag_clamp);\n        ImGui::SameLine(); HelpMarker(\n            \"As with every widget in dear imgui, we never modify values unless there is a user interaction.\\n\"\n            \"You can override the clamping limits by using CTRL+Click to input a value.\");\n        ImGui::DragScalar(\"drag s8\",        ImGuiDataType_S8,     &s8_v,  drag_speed, drag_clamp ? &s8_zero  : NULL, drag_clamp ? &s8_fifty  : NULL);\n        ImGui::DragScalar(\"drag u8\",        ImGuiDataType_U8,     &u8_v,  drag_speed, drag_clamp ? &u8_zero  : NULL, drag_clamp ? &u8_fifty  : NULL, \"%u ms\");\n        ImGui::DragScalar(\"drag s16\",       ImGuiDataType_S16,    &s16_v, drag_speed, drag_clamp ? &s16_zero : NULL, drag_clamp ? &s16_fifty : NULL);\n        ImGui::DragScalar(\"drag u16\",       ImGuiDataType_U16,    &u16_v, drag_speed, drag_clamp ? &u16_zero : NULL, drag_clamp ? &u16_fifty : NULL, \"%u ms\");\n        ImGui::DragScalar(\"drag s32\",       ImGuiDataType_S32,    &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL);\n        ImGui::DragScalar(\"drag s32 hex\",   ImGuiDataType_S32,    &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL, \"0x%08X\");\n        ImGui::DragScalar(\"drag u32\",       ImGuiDataType_U32,    &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, \"%u ms\");\n        ImGui::DragScalar(\"drag s64\",       ImGuiDataType_S64,    &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL);\n        ImGui::DragScalar(\"drag u64\",       ImGuiDataType_U64,    &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL);\n        ImGui::DragScalar(\"drag float\",     ImGuiDataType_Float,  &f32_v, 0.005f,  &f32_zero, &f32_one, \"%f\");\n        ImGui::DragScalar(\"drag float log\", ImGuiDataType_Float,  &f32_v, 0.005f,  &f32_zero, &f32_one, \"%f\", ImGuiSliderFlags_Logarithmic);\n        ImGui::DragScalar(\"drag double\",    ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL,     \"%.10f grams\");\n        ImGui::DragScalar(\"drag double log\",ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, \"0 < %.10f < 1\", ImGuiSliderFlags_Logarithmic);\n\n        IMGUI_DEMO_MARKER(\"Widgets/Data Types/Sliders\");\n        ImGui::SeparatorText(\"Sliders\");\n        ImGui::SliderScalar(\"slider s8 full\",       ImGuiDataType_S8,     &s8_v,  &s8_min,   &s8_max,   \"%d\");\n        ImGui::SliderScalar(\"slider u8 full\",       ImGuiDataType_U8,     &u8_v,  &u8_min,   &u8_max,   \"%u\");\n        ImGui::SliderScalar(\"slider s16 full\",      ImGuiDataType_S16,    &s16_v, &s16_min,  &s16_max,  \"%d\");\n        ImGui::SliderScalar(\"slider u16 full\",      ImGuiDataType_U16,    &u16_v, &u16_min,  &u16_max,  \"%u\");\n        ImGui::SliderScalar(\"slider s32 low\",       ImGuiDataType_S32,    &s32_v, &s32_zero, &s32_fifty,\"%d\");\n        ImGui::SliderScalar(\"slider s32 high\",      ImGuiDataType_S32,    &s32_v, &s32_hi_a, &s32_hi_b, \"%d\");\n        ImGui::SliderScalar(\"slider s32 full\",      ImGuiDataType_S32,    &s32_v, &s32_min,  &s32_max,  \"%d\");\n        ImGui::SliderScalar(\"slider s32 hex\",       ImGuiDataType_S32,    &s32_v, &s32_zero, &s32_fifty, \"0x%04X\");\n        ImGui::SliderScalar(\"slider u32 low\",       ImGuiDataType_U32,    &u32_v, &u32_zero, &u32_fifty,\"%u\");\n        ImGui::SliderScalar(\"slider u32 high\",      ImGuiDataType_U32,    &u32_v, &u32_hi_a, &u32_hi_b, \"%u\");\n        ImGui::SliderScalar(\"slider u32 full\",      ImGuiDataType_U32,    &u32_v, &u32_min,  &u32_max,  \"%u\");\n        ImGui::SliderScalar(\"slider s64 low\",       ImGuiDataType_S64,    &s64_v, &s64_zero, &s64_fifty,\"%\" PRId64);\n        ImGui::SliderScalar(\"slider s64 high\",      ImGuiDataType_S64,    &s64_v, &s64_hi_a, &s64_hi_b, \"%\" PRId64);\n        ImGui::SliderScalar(\"slider s64 full\",      ImGuiDataType_S64,    &s64_v, &s64_min,  &s64_max,  \"%\" PRId64);\n        ImGui::SliderScalar(\"slider u64 low\",       ImGuiDataType_U64,    &u64_v, &u64_zero, &u64_fifty,\"%\" PRIu64 \" ms\");\n        ImGui::SliderScalar(\"slider u64 high\",      ImGuiDataType_U64,    &u64_v, &u64_hi_a, &u64_hi_b, \"%\" PRIu64 \" ms\");\n        ImGui::SliderScalar(\"slider u64 full\",      ImGuiDataType_U64,    &u64_v, &u64_min,  &u64_max,  \"%\" PRIu64 \" ms\");\n        ImGui::SliderScalar(\"slider float low\",     ImGuiDataType_Float,  &f32_v, &f32_zero, &f32_one);\n        ImGui::SliderScalar(\"slider float low log\", ImGuiDataType_Float,  &f32_v, &f32_zero, &f32_one,  \"%.10f\", ImGuiSliderFlags_Logarithmic);\n        ImGui::SliderScalar(\"slider float high\",    ImGuiDataType_Float,  &f32_v, &f32_lo_a, &f32_hi_a, \"%e\");\n        ImGui::SliderScalar(\"slider double low\",    ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one,  \"%.10f grams\");\n        ImGui::SliderScalar(\"slider double low log\",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one,  \"%.10f\", ImGuiSliderFlags_Logarithmic);\n        ImGui::SliderScalar(\"slider double high\",   ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, \"%e grams\");\n\n        ImGui::SeparatorText(\"Sliders (reverse)\");\n        ImGui::SliderScalar(\"slider s8 reverse\",    ImGuiDataType_S8,   &s8_v,  &s8_max,    &s8_min,   \"%d\");\n        ImGui::SliderScalar(\"slider u8 reverse\",    ImGuiDataType_U8,   &u8_v,  &u8_max,    &u8_min,   \"%u\");\n        ImGui::SliderScalar(\"slider s32 reverse\",   ImGuiDataType_S32,  &s32_v, &s32_fifty, &s32_zero, \"%d\");\n        ImGui::SliderScalar(\"slider u32 reverse\",   ImGuiDataType_U32,  &u32_v, &u32_fifty, &u32_zero, \"%u\");\n        ImGui::SliderScalar(\"slider s64 reverse\",   ImGuiDataType_S64,  &s64_v, &s64_fifty, &s64_zero, \"%\" PRId64);\n        ImGui::SliderScalar(\"slider u64 reverse\",   ImGuiDataType_U64,  &u64_v, &u64_fifty, &u64_zero, \"%\" PRIu64 \" ms\");\n\n        IMGUI_DEMO_MARKER(\"Widgets/Data Types/Inputs\");\n        static bool inputs_step = true;\n        ImGui::SeparatorText(\"Inputs\");\n        ImGui::Checkbox(\"Show step buttons\", &inputs_step);\n        ImGui::InputScalar(\"input s8\",      ImGuiDataType_S8,     &s8_v,  inputs_step ? &s8_one  : NULL, NULL, \"%d\");\n        ImGui::InputScalar(\"input u8\",      ImGuiDataType_U8,     &u8_v,  inputs_step ? &u8_one  : NULL, NULL, \"%u\");\n        ImGui::InputScalar(\"input s16\",     ImGuiDataType_S16,    &s16_v, inputs_step ? &s16_one : NULL, NULL, \"%d\");\n        ImGui::InputScalar(\"input u16\",     ImGuiDataType_U16,    &u16_v, inputs_step ? &u16_one : NULL, NULL, \"%u\");\n        ImGui::InputScalar(\"input s32\",     ImGuiDataType_S32,    &s32_v, inputs_step ? &s32_one : NULL, NULL, \"%d\");\n        ImGui::InputScalar(\"input s32 hex\", ImGuiDataType_S32,    &s32_v, inputs_step ? &s32_one : NULL, NULL, \"%04X\");\n        ImGui::InputScalar(\"input u32\",     ImGuiDataType_U32,    &u32_v, inputs_step ? &u32_one : NULL, NULL, \"%u\");\n        ImGui::InputScalar(\"input u32 hex\", ImGuiDataType_U32,    &u32_v, inputs_step ? &u32_one : NULL, NULL, \"%08X\");\n        ImGui::InputScalar(\"input s64\",     ImGuiDataType_S64,    &s64_v, inputs_step ? &s64_one : NULL);\n        ImGui::InputScalar(\"input u64\",     ImGuiDataType_U64,    &u64_v, inputs_step ? &u64_one : NULL);\n        ImGui::InputScalar(\"input float\",   ImGuiDataType_Float,  &f32_v, inputs_step ? &f32_one : NULL);\n        ImGui::InputScalar(\"input double\",  ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL);\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Multi-component Widgets\");\n    if (ImGui::TreeNode(\"Multi-component Widgets\"))\n    {\n        static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f };\n        static int vec4i[4] = { 1, 5, 100, 255 };\n\n        ImGui::SeparatorText(\"2-wide\");\n        ImGui::InputFloat2(\"input float2\", vec4f);\n        ImGui::DragFloat2(\"drag float2\", vec4f, 0.01f, 0.0f, 1.0f);\n        ImGui::SliderFloat2(\"slider float2\", vec4f, 0.0f, 1.0f);\n        ImGui::InputInt2(\"input int2\", vec4i);\n        ImGui::DragInt2(\"drag int2\", vec4i, 1, 0, 255);\n        ImGui::SliderInt2(\"slider int2\", vec4i, 0, 255);\n\n        ImGui::SeparatorText(\"3-wide\");\n        ImGui::InputFloat3(\"input float3\", vec4f);\n        ImGui::DragFloat3(\"drag float3\", vec4f, 0.01f, 0.0f, 1.0f);\n        ImGui::SliderFloat3(\"slider float3\", vec4f, 0.0f, 1.0f);\n        ImGui::InputInt3(\"input int3\", vec4i);\n        ImGui::DragInt3(\"drag int3\", vec4i, 1, 0, 255);\n        ImGui::SliderInt3(\"slider int3\", vec4i, 0, 255);\n\n        ImGui::SeparatorText(\"4-wide\");\n        ImGui::InputFloat4(\"input float4\", vec4f);\n        ImGui::DragFloat4(\"drag float4\", vec4f, 0.01f, 0.0f, 1.0f);\n        ImGui::SliderFloat4(\"slider float4\", vec4f, 0.0f, 1.0f);\n        ImGui::InputInt4(\"input int4\", vec4i);\n        ImGui::DragInt4(\"drag int4\", vec4i, 1, 0, 255);\n        ImGui::SliderInt4(\"slider int4\", vec4i, 0, 255);\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Vertical Sliders\");\n    if (ImGui::TreeNode(\"Vertical Sliders\"))\n    {\n        const float spacing = 4;\n        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing));\n\n        static int int_value = 0;\n        ImGui::VSliderInt(\"##int\", ImVec2(18, 160), &int_value, 0, 5);\n        ImGui::SameLine();\n\n        static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f };\n        ImGui::PushID(\"set1\");\n        for (int i = 0; i < 7; i++)\n        {\n            if (i > 0) ImGui::SameLine();\n            ImGui::PushID(i);\n            ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i / 7.0f, 0.5f, 0.5f));\n            ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.5f));\n            ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.5f));\n            ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i / 7.0f, 0.9f, 0.9f));\n            ImGui::VSliderFloat(\"##v\", ImVec2(18, 160), &values[i], 0.0f, 1.0f, \"\");\n            if (ImGui::IsItemActive() || ImGui::IsItemHovered())\n                ImGui::SetTooltip(\"%.3f\", values[i]);\n            ImGui::PopStyleColor(4);\n            ImGui::PopID();\n        }\n        ImGui::PopID();\n\n        ImGui::SameLine();\n        ImGui::PushID(\"set2\");\n        static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f };\n        const int rows = 3;\n        const ImVec2 small_slider_size(18, (float)(int)((160.0f - (rows - 1) * spacing) / rows));\n        for (int nx = 0; nx < 4; nx++)\n        {\n            if (nx > 0) ImGui::SameLine();\n            ImGui::BeginGroup();\n            for (int ny = 0; ny < rows; ny++)\n            {\n                ImGui::PushID(nx * rows + ny);\n                ImGui::VSliderFloat(\"##v\", small_slider_size, &values2[nx], 0.0f, 1.0f, \"\");\n                if (ImGui::IsItemActive() || ImGui::IsItemHovered())\n                    ImGui::SetTooltip(\"%.3f\", values2[nx]);\n                ImGui::PopID();\n            }\n            ImGui::EndGroup();\n        }\n        ImGui::PopID();\n\n        ImGui::SameLine();\n        ImGui::PushID(\"set3\");\n        for (int i = 0; i < 4; i++)\n        {\n            if (i > 0) ImGui::SameLine();\n            ImGui::PushID(i);\n            ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40);\n            ImGui::VSliderFloat(\"##v\", ImVec2(40, 160), &values[i], 0.0f, 1.0f, \"%.2f\\nsec\");\n            ImGui::PopStyleVar();\n            ImGui::PopID();\n        }\n        ImGui::PopID();\n        ImGui::PopStyleVar();\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Drag and drop\");\n    if (ImGui::TreeNode(\"Drag and Drop\"))\n    {\n        IMGUI_DEMO_MARKER(\"Widgets/Drag and drop/Standard widgets\");\n        if (ImGui::TreeNode(\"Drag and drop in standard widgets\"))\n        {\n            // ColorEdit widgets automatically act as drag source and drag target.\n            // They are using standardized payload strings IMGUI_PAYLOAD_TYPE_COLOR_3F and IMGUI_PAYLOAD_TYPE_COLOR_4F\n            // to allow your own widgets to use colors in their drag and drop interaction.\n            // Also see 'Demo->Widgets->Color/Picker Widgets->Palette' demo.\n            HelpMarker(\"You can drag from the color squares.\");\n            static float col1[3] = { 1.0f, 0.0f, 0.2f };\n            static float col2[4] = { 0.4f, 0.7f, 0.0f, 0.5f };\n            ImGui::ColorEdit3(\"color 1\", col1);\n            ImGui::ColorEdit4(\"color 2\", col2);\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Drag and drop/Copy-swap items\");\n        if (ImGui::TreeNode(\"Drag and drop to copy/swap items\"))\n        {\n            enum Mode\n            {\n                Mode_Copy,\n                Mode_Move,\n                Mode_Swap\n            };\n            static int mode = 0;\n            if (ImGui::RadioButton(\"Copy\", mode == Mode_Copy)) { mode = Mode_Copy; } ImGui::SameLine();\n            if (ImGui::RadioButton(\"Move\", mode == Mode_Move)) { mode = Mode_Move; } ImGui::SameLine();\n            if (ImGui::RadioButton(\"Swap\", mode == Mode_Swap)) { mode = Mode_Swap; }\n            static const char* names[9] =\n            {\n                \"Bobby\", \"Beatrice\", \"Betty\",\n                \"Brianna\", \"Barry\", \"Bernard\",\n                \"Bibi\", \"Blaine\", \"Bryn\"\n            };\n            for (int n = 0; n < IM_ARRAYSIZE(names); n++)\n            {\n                ImGui::PushID(n);\n                if ((n % 3) != 0)\n                    ImGui::SameLine();\n                ImGui::Button(names[n], ImVec2(60, 60));\n\n                // Our buttons are both drag sources and drag targets here!\n                if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))\n                {\n                    // Set payload to carry the index of our item (could be anything)\n                    ImGui::SetDragDropPayload(\"DND_DEMO_CELL\", &n, sizeof(int));\n\n                    // Display preview (could be anything, e.g. when dragging an image we could decide to display\n                    // the filename and a small preview of the image, etc.)\n                    if (mode == Mode_Copy) { ImGui::Text(\"Copy %s\", names[n]); }\n                    if (mode == Mode_Move) { ImGui::Text(\"Move %s\", names[n]); }\n                    if (mode == Mode_Swap) { ImGui::Text(\"Swap %s\", names[n]); }\n                    ImGui::EndDragDropSource();\n                }\n                if (ImGui::BeginDragDropTarget())\n                {\n                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(\"DND_DEMO_CELL\"))\n                    {\n                        IM_ASSERT(payload->DataSize == sizeof(int));\n                        int payload_n = *(const int*)payload->Data;\n                        if (mode == Mode_Copy)\n                        {\n                            names[n] = names[payload_n];\n                        }\n                        if (mode == Mode_Move)\n                        {\n                            names[n] = names[payload_n];\n                            names[payload_n] = \"\";\n                        }\n                        if (mode == Mode_Swap)\n                        {\n                            const char* tmp = names[n];\n                            names[n] = names[payload_n];\n                            names[payload_n] = tmp;\n                        }\n                    }\n                    ImGui::EndDragDropTarget();\n                }\n                ImGui::PopID();\n            }\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Drag and Drop/Drag to reorder items (simple)\");\n        if (ImGui::TreeNode(\"Drag to reorder items (simple)\"))\n        {\n            // Simple reordering\n            HelpMarker(\n                \"We don't use the drag and drop api at all here! \"\n                \"Instead we query when the item is held but not hovered, and order items accordingly.\");\n            static const char* item_names[] = { \"Item One\", \"Item Two\", \"Item Three\", \"Item Four\", \"Item Five\" };\n            for (int n = 0; n < IM_ARRAYSIZE(item_names); n++)\n            {\n                const char* item = item_names[n];\n                ImGui::Selectable(item);\n\n                if (ImGui::IsItemActive() && !ImGui::IsItemHovered())\n                {\n                    int n_next = n + (ImGui::GetMouseDragDelta(0).y < 0.f ? -1 : 1);\n                    if (n_next >= 0 && n_next < IM_ARRAYSIZE(item_names))\n                    {\n                        item_names[n] = item_names[n_next];\n                        item_names[n_next] = item;\n                        ImGui::ResetMouseDragDelta();\n                    }\n                }\n            }\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Widgets/Drag and Drop/Tooltip at target location\");\n        if (ImGui::TreeNode(\"Tooltip at target location\"))\n        {\n            for (int n = 0; n < 2; n++)\n            {\n                // Drop targets\n                ImGui::Button(n ? \"drop here##1\" : \"drop here##0\");\n                if (ImGui::BeginDragDropTarget())\n                {\n                    ImGuiDragDropFlags drop_target_flags = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoPreviewTooltip;\n                    if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, drop_target_flags))\n                    {\n                        IM_UNUSED(payload);\n                        ImGui::SetMouseCursor(ImGuiMouseCursor_NotAllowed);\n                        ImGui::BeginTooltip();\n                        ImGui::Text(\"Cannot drop here!\");\n                        ImGui::EndTooltip();\n                    }\n                    ImGui::EndDragDropTarget();\n                }\n\n                // Drop source\n                static ImVec4 col4 = { 1.0f, 0.0f, 0.2f, 1.0f };\n                if (n == 0)\n                    ImGui::ColorButton(\"drag me\", col4);\n\n            }\n            ImGui::TreePop();\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Querying Item Status (Edited,Active,Hovered etc.)\");\n    if (ImGui::TreeNode(\"Querying Item Status (Edited/Active/Hovered etc.)\"))\n    {\n        // Select an item type\n        const char* item_names[] =\n        {\n            \"Text\", \"Button\", \"Button (w/ repeat)\", \"Checkbox\", \"SliderFloat\", \"InputText\", \"InputTextMultiline\", \"InputFloat\",\n            \"InputFloat3\", \"ColorEdit4\", \"Selectable\", \"MenuItem\", \"TreeNode\", \"TreeNode (w/ double-click)\", \"Combo\", \"ListBox\"\n        };\n        static int item_type = 4;\n        static bool item_disabled = false;\n        ImGui::Combo(\"Item Type\", &item_type, item_names, IM_ARRAYSIZE(item_names), IM_ARRAYSIZE(item_names));\n        ImGui::SameLine();\n        HelpMarker(\"Testing how various types of items are interacting with the IsItemXXX functions. Note that the bool return value of most ImGui function is generally equivalent to calling ImGui::IsItemHovered().\");\n        ImGui::Checkbox(\"Item Disabled\",  &item_disabled);\n\n        // Submit selected items so we can query their status in the code following it.\n        bool ret = false;\n        static bool b = false;\n        static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f };\n        static char str[16] = {};\n        if (item_disabled)\n            ImGui::BeginDisabled(true);\n        if (item_type == 0) { ImGui::Text(\"ITEM: Text\"); }                                              // Testing text items with no identifier/interaction\n        if (item_type == 1) { ret = ImGui::Button(\"ITEM: Button\"); }                                    // Testing button\n        if (item_type == 2) { ImGui::PushButtonRepeat(true); ret = ImGui::Button(\"ITEM: Button\"); ImGui::PopButtonRepeat(); } // Testing button (with repeater)\n        if (item_type == 3) { ret = ImGui::Checkbox(\"ITEM: Checkbox\", &b); }                            // Testing checkbox\n        if (item_type == 4) { ret = ImGui::SliderFloat(\"ITEM: SliderFloat\", &col4f[0], 0.0f, 1.0f); }   // Testing basic item\n        if (item_type == 5) { ret = ImGui::InputText(\"ITEM: InputText\", &str[0], IM_ARRAYSIZE(str)); }  // Testing input text (which handles tabbing)\n        if (item_type == 6) { ret = ImGui::InputTextMultiline(\"ITEM: InputTextMultiline\", &str[0], IM_ARRAYSIZE(str)); } // Testing input text (which uses a child window)\n        if (item_type == 7) { ret = ImGui::InputFloat(\"ITEM: InputFloat\", col4f, 1.0f); }               // Testing +/- buttons on scalar input\n        if (item_type == 8) { ret = ImGui::InputFloat3(\"ITEM: InputFloat3\", col4f); }                   // Testing multi-component items (IsItemXXX flags are reported merged)\n        if (item_type == 9) { ret = ImGui::ColorEdit4(\"ITEM: ColorEdit4\", col4f); }                     // Testing multi-component items (IsItemXXX flags are reported merged)\n        if (item_type == 10){ ret = ImGui::Selectable(\"ITEM: Selectable\"); }                            // Testing selectable item\n        if (item_type == 11){ ret = ImGui::MenuItem(\"ITEM: MenuItem\"); }                                // Testing menu item (they use ImGuiButtonFlags_PressedOnRelease button policy)\n        if (item_type == 12){ ret = ImGui::TreeNode(\"ITEM: TreeNode\"); if (ret) ImGui::TreePop(); }     // Testing tree node\n        if (item_type == 13){ ret = ImGui::TreeNodeEx(\"ITEM: TreeNode w/ ImGuiTreeNodeFlags_OpenOnDoubleClick\", ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_NoTreePushOnOpen); } // Testing tree node with ImGuiButtonFlags_PressedOnDoubleClick button policy.\n        if (item_type == 14){ const char* items[] = { \"Apple\", \"Banana\", \"Cherry\", \"Kiwi\" }; static int current = 1; ret = ImGui::Combo(\"ITEM: Combo\", &current, items, IM_ARRAYSIZE(items)); }\n        if (item_type == 15){ const char* items[] = { \"Apple\", \"Banana\", \"Cherry\", \"Kiwi\" }; static int current = 1; ret = ImGui::ListBox(\"ITEM: ListBox\", &current, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); }\n\n        bool hovered_delay_none = ImGui::IsItemHovered();\n        bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary);\n        bool hovered_delay_short = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort);\n        bool hovered_delay_normal = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal);\n        bool hovered_delay_tooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary\n\n        // Display the values of IsItemHovered() and other common item state functions.\n        // Note that the ImGuiHoveredFlags_XXX flags can be combined.\n        // Because BulletText is an item itself and that would affect the output of IsItemXXX functions,\n        // we query every state in a single call to avoid storing them and to simplify the code.\n        ImGui::BulletText(\n            \"Return value = %d\\n\"\n            \"IsItemFocused() = %d\\n\"\n            \"IsItemHovered() = %d\\n\"\n            \"IsItemHovered(_AllowWhenBlockedByPopup) = %d\\n\"\n            \"IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\\n\"\n            \"IsItemHovered(_AllowWhenOverlappedByItem) = %d\\n\"\n            \"IsItemHovered(_AllowWhenOverlappedByWindow) = %d\\n\"\n            \"IsItemHovered(_AllowWhenDisabled) = %d\\n\"\n            \"IsItemHovered(_RectOnly) = %d\\n\"\n            \"IsItemActive() = %d\\n\"\n            \"IsItemEdited() = %d\\n\"\n            \"IsItemActivated() = %d\\n\"\n            \"IsItemDeactivated() = %d\\n\"\n            \"IsItemDeactivatedAfterEdit() = %d\\n\"\n            \"IsItemVisible() = %d\\n\"\n            \"IsItemClicked() = %d\\n\"\n            \"IsItemToggledOpen() = %d\\n\"\n            \"GetItemRectMin() = (%.1f, %.1f)\\n\"\n            \"GetItemRectMax() = (%.1f, %.1f)\\n\"\n            \"GetItemRectSize() = (%.1f, %.1f)\",\n            ret,\n            ImGui::IsItemFocused(),\n            ImGui::IsItemHovered(),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByItem),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlappedByWindow),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled),\n            ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly),\n            ImGui::IsItemActive(),\n            ImGui::IsItemEdited(),\n            ImGui::IsItemActivated(),\n            ImGui::IsItemDeactivated(),\n            ImGui::IsItemDeactivatedAfterEdit(),\n            ImGui::IsItemVisible(),\n            ImGui::IsItemClicked(),\n            ImGui::IsItemToggledOpen(),\n            ImGui::GetItemRectMin().x, ImGui::GetItemRectMin().y,\n            ImGui::GetItemRectMax().x, ImGui::GetItemRectMax().y,\n            ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y\n        );\n        ImGui::BulletText(\n            \"with Hovering Delay or Stationary test:\\n\"\n            \"IsItemHovered() = = %d\\n\"\n            \"IsItemHovered(_Stationary) = %d\\n\"\n            \"IsItemHovered(_DelayShort) = %d\\n\"\n            \"IsItemHovered(_DelayNormal) = %d\\n\"\n            \"IsItemHovered(_Tooltip) = %d\",\n            hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip);\n\n        if (item_disabled)\n            ImGui::EndDisabled();\n\n        char buf[1] = \"\";\n        ImGui::InputText(\"unused\", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly);\n        ImGui::SameLine();\n        HelpMarker(\"This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status.\");\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Querying Window Status (Focused,Hovered etc.)\");\n    if (ImGui::TreeNode(\"Querying Window Status (Focused/Hovered etc.)\"))\n    {\n        static bool embed_all_inside_a_child_window = false;\n        ImGui::Checkbox(\"Embed everything inside a child window for testing _RootWindow flag.\", &embed_all_inside_a_child_window);\n        if (embed_all_inside_a_child_window)\n            ImGui::BeginChild(\"outer_child\", ImVec2(0, ImGui::GetFontSize() * 20.0f), ImGuiChildFlags_Border);\n\n        // Testing IsWindowFocused() function with its various flags.\n        ImGui::BulletText(\n            \"IsWindowFocused() = %d\\n\"\n            \"IsWindowFocused(_ChildWindows) = %d\\n\"\n            \"IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowFocused(_ChildWindows|_RootWindow) = %d\\n\"\n            \"IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowFocused(_RootWindow) = %d\\n\"\n            \"IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowFocused(_AnyWindow) = %d\\n\",\n            ImGui::IsWindowFocused(),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),\n            ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow));\n\n        // Testing IsWindowHovered() function with its various flags.\n        ImGui::BulletText(\n            \"IsWindowHovered() = %d\\n\"\n            \"IsWindowHovered(_AllowWhenBlockedByPopup) = %d\\n\"\n            \"IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows|_RootWindow) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowHovered(_RootWindow) = %d\\n\"\n            \"IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\\n\"\n            \"IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\\n\"\n            \"IsWindowHovered(_AnyWindow) = %d\\n\"\n            \"IsWindowHovered(_Stationary) = %d\\n\",\n            ImGui::IsWindowHovered(),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow),\n            ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary));\n\n        ImGui::BeginChild(\"child\", ImVec2(0, 50), ImGuiChildFlags_Border);\n        ImGui::Text(\"This is another child window for testing the _ChildWindows flag.\");\n        ImGui::EndChild();\n        if (embed_all_inside_a_child_window)\n            ImGui::EndChild();\n\n        // Calling IsItemHovered() after begin returns the hovered status of the title bar.\n        // This is useful in particular if you want to create a context menu associated to the title bar of a window.\n        static bool test_window = false;\n        ImGui::Checkbox(\"Hovered/Active tests after Begin() for title bar testing\", &test_window);\n        if (test_window)\n        {\n            ImGui::Begin(\"Title bar Hovered/Active tests\", &test_window);\n            if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered()\n            {\n                if (ImGui::MenuItem(\"Close\")) { test_window = false; }\n                ImGui::EndPopup();\n            }\n            ImGui::Text(\n                \"IsItemHovered() after begin = %d (== is title bar hovered)\\n\"\n                \"IsItemActive() after begin = %d (== is window being clicked/moved)\\n\",\n                ImGui::IsItemHovered(), ImGui::IsItemActive());\n            ImGui::End();\n        }\n\n        ImGui::TreePop();\n    }\n\n    // Demonstrate BeginDisabled/EndDisabled using a checkbox located at the bottom of the section (which is a bit odd:\n    // logically we'd have this checkbox at the top of the section, but we don't want this feature to steal that space)\n    if (disable_all)\n        ImGui::EndDisabled();\n\n    IMGUI_DEMO_MARKER(\"Widgets/Disable Block\");\n    if (ImGui::TreeNode(\"Disable block\"))\n    {\n        ImGui::Checkbox(\"Disable entire section above\", &disable_all);\n        ImGui::SameLine(); HelpMarker(\"Demonstrate using BeginDisabled()/EndDisabled() across this section.\");\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Widgets/Text Filter\");\n    if (ImGui::TreeNode(\"Text Filter\"))\n    {\n        // Helper class to easy setup a text filter.\n        // You may want to implement a more feature-full filtering scheme in your own application.\n        HelpMarker(\"Not a widget per-se, but ImGuiTextFilter is a helper to perform simple filtering on text strings.\");\n        static ImGuiTextFilter filter;\n        ImGui::Text(\"Filter usage:\\n\"\n            \"  \\\"\\\"         display all lines\\n\"\n            \"  \\\"xxx\\\"      display lines containing \\\"xxx\\\"\\n\"\n            \"  \\\"xxx,yyy\\\"  display lines containing \\\"xxx\\\" or \\\"yyy\\\"\\n\"\n            \"  \\\"-xxx\\\"     hide lines containing \\\"xxx\\\"\");\n        filter.Draw();\n        const char* lines[] = { \"aaa1.c\", \"bbb1.c\", \"ccc1.c\", \"aaa2.cpp\", \"bbb2.cpp\", \"ccc2.cpp\", \"abc.h\", \"hello, world\" };\n        for (int i = 0; i < IM_ARRAYSIZE(lines); i++)\n            if (filter.PassFilter(lines[i]))\n                ImGui::BulletText(\"%s\", lines[i]);\n        ImGui::TreePop();\n    }\n}\n\nstatic void ShowDemoWindowLayout()\n{\n    IMGUI_DEMO_MARKER(\"Layout\");\n    if (!ImGui::CollapsingHeader(\"Layout & Scrolling\"))\n        return;\n\n    IMGUI_DEMO_MARKER(\"Layout/Child windows\");\n    if (ImGui::TreeNode(\"Child windows\"))\n    {\n        ImGui::SeparatorText(\"Child windows\");\n\n        HelpMarker(\"Use child windows to begin into a self-contained independent scrolling/clipping regions within a host window.\");\n        static bool disable_mouse_wheel = false;\n        static bool disable_menu = false;\n        ImGui::Checkbox(\"Disable Mouse Wheel\", &disable_mouse_wheel);\n        ImGui::Checkbox(\"Disable Menu\", &disable_menu);\n\n        // Child 1: no border, enable horizontal scrollbar\n        {\n            ImGuiWindowFlags window_flags = ImGuiWindowFlags_HorizontalScrollbar;\n            if (disable_mouse_wheel)\n                window_flags |= ImGuiWindowFlags_NoScrollWithMouse;\n            ImGui::BeginChild(\"ChildL\", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 260), ImGuiChildFlags_None, window_flags);\n            for (int i = 0; i < 100; i++)\n                ImGui::Text(\"%04d: scrollable region\", i);\n            ImGui::EndChild();\n        }\n\n        ImGui::SameLine();\n\n        // Child 2: rounded border\n        {\n            ImGuiWindowFlags window_flags = ImGuiWindowFlags_None;\n            if (disable_mouse_wheel)\n                window_flags |= ImGuiWindowFlags_NoScrollWithMouse;\n            if (!disable_menu)\n                window_flags |= ImGuiWindowFlags_MenuBar;\n            ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);\n            ImGui::BeginChild(\"ChildR\", ImVec2(0, 260), ImGuiChildFlags_Border, window_flags);\n            if (!disable_menu && ImGui::BeginMenuBar())\n            {\n                if (ImGui::BeginMenu(\"Menu\"))\n                {\n                    ShowExampleMenuFile();\n                    ImGui::EndMenu();\n                }\n                ImGui::EndMenuBar();\n            }\n            if (ImGui::BeginTable(\"split\", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings))\n            {\n                for (int i = 0; i < 100; i++)\n                {\n                    char buf[32];\n                    sprintf(buf, \"%03d\", i);\n                    ImGui::TableNextColumn();\n                    ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));\n                }\n                ImGui::EndTable();\n            }\n            ImGui::EndChild();\n            ImGui::PopStyleVar();\n        }\n\n        // Child 3: manual-resize\n        ImGui::SeparatorText(\"Manual-resize\");\n        {\n            HelpMarker(\"Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents.\");\n            ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));\n            if (ImGui::BeginChild(\"ResizableChild\", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY))\n                for (int n = 0; n < 10; n++)\n                    ImGui::Text(\"Line %04d\", n);\n            ImGui::PopStyleColor();\n            ImGui::EndChild();\n        }\n\n        // Child 4: auto-resizing height with a limit\n        ImGui::SeparatorText(\"Auto-resize with constraints\");\n        {\n            static int draw_lines = 3;\n            static int max_height_in_lines = 10;\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n            ImGui::DragInt(\"Lines Count\", &draw_lines, 0.2f);\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n            ImGui::DragInt(\"Max Height (in Lines)\", &max_height_in_lines, 0.2f);\n\n            ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 1), ImVec2(FLT_MAX, ImGui::GetTextLineHeightWithSpacing() * max_height_in_lines));\n            if (ImGui::BeginChild(\"ConstrainedChild\", ImVec2(-FLT_MIN, 0.0f), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY))\n                for (int n = 0; n < draw_lines; n++)\n                    ImGui::Text(\"Line %04d\", n);\n            ImGui::EndChild();\n        }\n\n        ImGui::SeparatorText(\"Misc/Advanced\");\n\n        // Demonstrate a few extra things\n        // - Changing ImGuiCol_ChildBg (which is transparent black in default styles)\n        // - Using SetCursorPos() to position child window (the child window is an item from the POV of parent window)\n        //   You can also call SetNextWindowPos() to position the child window. The parent window will effectively\n        //   layout from this position.\n        // - Using ImGui::GetItemRectMin/Max() to query the \"item\" state (because the child window is an item from\n        //   the POV of the parent window). See 'Demo->Querying Status (Edited/Active/Hovered etc.)' for details.\n        {\n            static int offset_x = 0;\n            static bool override_bg_color = true;\n            static ImGuiChildFlags child_flags = ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX | ImGuiChildFlags_ResizeY;\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n            ImGui::DragInt(\"Offset X\", &offset_x, 1.0f, -1000, 1000);\n            ImGui::Checkbox(\"Override ChildBg color\", &override_bg_color);\n            ImGui::CheckboxFlags(\"ImGuiChildFlags_Border\", &child_flags, ImGuiChildFlags_Border);\n            ImGui::CheckboxFlags(\"ImGuiChildFlags_AlwaysUseWindowPadding\", &child_flags, ImGuiChildFlags_AlwaysUseWindowPadding);\n            ImGui::CheckboxFlags(\"ImGuiChildFlags_ResizeX\", &child_flags, ImGuiChildFlags_ResizeX);\n            ImGui::CheckboxFlags(\"ImGuiChildFlags_ResizeY\", &child_flags, ImGuiChildFlags_ResizeY);\n            ImGui::CheckboxFlags(\"ImGuiChildFlags_FrameStyle\", &child_flags, ImGuiChildFlags_FrameStyle);\n            ImGui::SameLine(); HelpMarker(\"Style the child window like a framed item: use FrameBg, FrameRounding, FrameBorderSize, FramePadding instead of ChildBg, ChildRounding, ChildBorderSize, WindowPadding.\");\n            if (child_flags & ImGuiChildFlags_FrameStyle)\n                override_bg_color = false;\n\n            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (float)offset_x);\n            if (override_bg_color)\n                ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(255, 0, 0, 100));\n            ImGui::BeginChild(\"Red\", ImVec2(200, 100), child_flags, ImGuiWindowFlags_None);\n            if (override_bg_color)\n                ImGui::PopStyleColor();\n\n            for (int n = 0; n < 50; n++)\n                ImGui::Text(\"Some test %d\", n);\n            ImGui::EndChild();\n            bool child_is_hovered = ImGui::IsItemHovered();\n            ImVec2 child_rect_min = ImGui::GetItemRectMin();\n            ImVec2 child_rect_max = ImGui::GetItemRectMax();\n            ImGui::Text(\"Hovered: %d\", child_is_hovered);\n            ImGui::Text(\"Rect of child window is: (%.0f,%.0f) (%.0f,%.0f)\", child_rect_min.x, child_rect_min.y, child_rect_max.x, child_rect_max.y);\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Layout/Widgets Width\");\n    if (ImGui::TreeNode(\"Widgets Width\"))\n    {\n        static float f = 0.0f;\n        static bool show_indented_items = true;\n        ImGui::Checkbox(\"Show indented items\", &show_indented_items);\n\n        // Use SetNextItemWidth() to set the width of a single upcoming item.\n        // Use PushItemWidth()/PopItemWidth() to set the width of a group of items.\n        // In real code use you'll probably want to choose width values that are proportional to your font size\n        // e.g. Using '20.0f * GetFontSize()' as width instead of '200.0f', etc.\n\n        ImGui::Text(\"SetNextItemWidth/PushItemWidth(100)\");\n        ImGui::SameLine(); HelpMarker(\"Fixed width.\");\n        ImGui::PushItemWidth(100);\n        ImGui::DragFloat(\"float##1b\", &f);\n        if (show_indented_items)\n        {\n            ImGui::Indent();\n            ImGui::DragFloat(\"float (indented)##1b\", &f);\n            ImGui::Unindent();\n        }\n        ImGui::PopItemWidth();\n\n        ImGui::Text(\"SetNextItemWidth/PushItemWidth(-100)\");\n        ImGui::SameLine(); HelpMarker(\"Align to right edge minus 100\");\n        ImGui::PushItemWidth(-100);\n        ImGui::DragFloat(\"float##2a\", &f);\n        if (show_indented_items)\n        {\n            ImGui::Indent();\n            ImGui::DragFloat(\"float (indented)##2b\", &f);\n            ImGui::Unindent();\n        }\n        ImGui::PopItemWidth();\n\n        ImGui::Text(\"SetNextItemWidth/PushItemWidth(GetContentRegionAvail().x * 0.5f)\");\n        ImGui::SameLine(); HelpMarker(\"Half of available width.\\n(~ right-cursor_pos)\\n(works within a column set)\");\n        ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f);\n        ImGui::DragFloat(\"float##3a\", &f);\n        if (show_indented_items)\n        {\n            ImGui::Indent();\n            ImGui::DragFloat(\"float (indented)##3b\", &f);\n            ImGui::Unindent();\n        }\n        ImGui::PopItemWidth();\n\n        ImGui::Text(\"SetNextItemWidth/PushItemWidth(-GetContentRegionAvail().x * 0.5f)\");\n        ImGui::SameLine(); HelpMarker(\"Align to right edge minus half\");\n        ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);\n        ImGui::DragFloat(\"float##4a\", &f);\n        if (show_indented_items)\n        {\n            ImGui::Indent();\n            ImGui::DragFloat(\"float (indented)##4b\", &f);\n            ImGui::Unindent();\n        }\n        ImGui::PopItemWidth();\n\n        // Demonstrate using PushItemWidth to surround three items.\n        // Calling SetNextItemWidth() before each of them would have the same effect.\n        ImGui::Text(\"SetNextItemWidth/PushItemWidth(-FLT_MIN)\");\n        ImGui::SameLine(); HelpMarker(\"Align to right edge\");\n        ImGui::PushItemWidth(-FLT_MIN);\n        ImGui::DragFloat(\"##float5a\", &f);\n        if (show_indented_items)\n        {\n            ImGui::Indent();\n            ImGui::DragFloat(\"float (indented)##5b\", &f);\n            ImGui::Unindent();\n        }\n        ImGui::PopItemWidth();\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout\");\n    if (ImGui::TreeNode(\"Basic Horizontal Layout\"))\n    {\n        ImGui::TextWrapped(\"(Use ImGui::SameLine() to keep adding items to the right of the preceding item)\");\n\n        // Text\n        IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout/SameLine\");\n        ImGui::Text(\"Two items: Hello\"); ImGui::SameLine();\n        ImGui::TextColored(ImVec4(1, 1, 0, 1), \"Sailor\");\n\n        // Adjust spacing\n        ImGui::Text(\"More spacing: Hello\"); ImGui::SameLine(0, 20);\n        ImGui::TextColored(ImVec4(1, 1, 0, 1), \"Sailor\");\n\n        // Button\n        ImGui::AlignTextToFramePadding();\n        ImGui::Text(\"Normal buttons\"); ImGui::SameLine();\n        ImGui::Button(\"Banana\"); ImGui::SameLine();\n        ImGui::Button(\"Apple\"); ImGui::SameLine();\n        ImGui::Button(\"Corniflower\");\n\n        // Button\n        ImGui::Text(\"Small buttons\"); ImGui::SameLine();\n        ImGui::SmallButton(\"Like this one\"); ImGui::SameLine();\n        ImGui::Text(\"can fit within a text block.\");\n\n        // Aligned to arbitrary position. Easy/cheap column.\n        IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout/SameLine (with offset)\");\n        ImGui::Text(\"Aligned\");\n        ImGui::SameLine(150); ImGui::Text(\"x=150\");\n        ImGui::SameLine(300); ImGui::Text(\"x=300\");\n        ImGui::Text(\"Aligned\");\n        ImGui::SameLine(150); ImGui::SmallButton(\"x=150\");\n        ImGui::SameLine(300); ImGui::SmallButton(\"x=300\");\n\n        // Checkbox\n        IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout/SameLine (more)\");\n        static bool c1 = false, c2 = false, c3 = false, c4 = false;\n        ImGui::Checkbox(\"My\", &c1); ImGui::SameLine();\n        ImGui::Checkbox(\"Tailor\", &c2); ImGui::SameLine();\n        ImGui::Checkbox(\"Is\", &c3); ImGui::SameLine();\n        ImGui::Checkbox(\"Rich\", &c4);\n\n        // Various\n        static float f0 = 1.0f, f1 = 2.0f, f2 = 3.0f;\n        ImGui::PushItemWidth(80);\n        const char* items[] = { \"AAAA\", \"BBBB\", \"CCCC\", \"DDDD\" };\n        static int item = -1;\n        ImGui::Combo(\"Combo\", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine();\n        ImGui::SliderFloat(\"X\", &f0, 0.0f, 5.0f); ImGui::SameLine();\n        ImGui::SliderFloat(\"Y\", &f1, 0.0f, 5.0f); ImGui::SameLine();\n        ImGui::SliderFloat(\"Z\", &f2, 0.0f, 5.0f);\n        ImGui::PopItemWidth();\n\n        ImGui::PushItemWidth(80);\n        ImGui::Text(\"Lists:\");\n        static int selection[4] = { 0, 1, 2, 3 };\n        for (int i = 0; i < 4; i++)\n        {\n            if (i > 0) ImGui::SameLine();\n            ImGui::PushID(i);\n            ImGui::ListBox(\"\", &selection[i], items, IM_ARRAYSIZE(items));\n            ImGui::PopID();\n            //ImGui::SetItemTooltip(\"ListBox %d hovered\", i);\n        }\n        ImGui::PopItemWidth();\n\n        // Dummy\n        IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout/Dummy\");\n        ImVec2 button_sz(40, 40);\n        ImGui::Button(\"A\", button_sz); ImGui::SameLine();\n        ImGui::Dummy(button_sz); ImGui::SameLine();\n        ImGui::Button(\"B\", button_sz);\n\n        // Manually wrapping\n        // (we should eventually provide this as an automatic layout feature, but for now you can do it manually)\n        IMGUI_DEMO_MARKER(\"Layout/Basic Horizontal Layout/Manual wrapping\");\n        ImGui::Text(\"Manual wrapping:\");\n        ImGuiStyle& style = ImGui::GetStyle();\n        int buttons_count = 20;\n        float window_visible_x2 = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x;\n        for (int n = 0; n < buttons_count; n++)\n        {\n            ImGui::PushID(n);\n            ImGui::Button(\"Box\", button_sz);\n            float last_button_x2 = ImGui::GetItemRectMax().x;\n            float next_button_x2 = last_button_x2 + style.ItemSpacing.x + button_sz.x; // Expected position if next button was on same line\n            if (n + 1 < buttons_count && next_button_x2 < window_visible_x2)\n                ImGui::SameLine();\n            ImGui::PopID();\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Layout/Groups\");\n    if (ImGui::TreeNode(\"Groups\"))\n    {\n        HelpMarker(\n            \"BeginGroup() basically locks the horizontal position for new line. \"\n            \"EndGroup() bundles the whole group so that you can use \\\"item\\\" functions such as \"\n            \"IsItemHovered()/IsItemActive() or SameLine() etc. on the whole group.\");\n        ImGui::BeginGroup();\n        {\n            ImGui::BeginGroup();\n            ImGui::Button(\"AAA\");\n            ImGui::SameLine();\n            ImGui::Button(\"BBB\");\n            ImGui::SameLine();\n            ImGui::BeginGroup();\n            ImGui::Button(\"CCC\");\n            ImGui::Button(\"DDD\");\n            ImGui::EndGroup();\n            ImGui::SameLine();\n            ImGui::Button(\"EEE\");\n            ImGui::EndGroup();\n            ImGui::SetItemTooltip(\"First group hovered\");\n        }\n        // Capture the group size and create widgets using the same size\n        ImVec2 size = ImGui::GetItemRectSize();\n        const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f };\n        ImGui::PlotHistogram(\"##values\", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size);\n\n        ImGui::Button(\"ACTION\", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));\n        ImGui::SameLine();\n        ImGui::Button(\"REACTION\", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x) * 0.5f, size.y));\n        ImGui::EndGroup();\n        ImGui::SameLine();\n\n        ImGui::Button(\"LEVERAGE\\nBUZZWORD\", size);\n        ImGui::SameLine();\n\n        if (ImGui::BeginListBox(\"List\", size))\n        {\n            ImGui::Selectable(\"Selected\", true);\n            ImGui::Selectable(\"Not Selected\", false);\n            ImGui::EndListBox();\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Layout/Text Baseline Alignment\");\n    if (ImGui::TreeNode(\"Text Baseline Alignment\"))\n    {\n        {\n            ImGui::BulletText(\"Text baseline:\");\n            ImGui::SameLine(); HelpMarker(\n                \"This is testing the vertical alignment that gets applied on text to keep it aligned with widgets. \"\n                \"Lines only composed of text or \\\"small\\\" widgets use less vertical space than lines with framed widgets.\");\n            ImGui::Indent();\n\n            ImGui::Text(\"KO Blahblah\"); ImGui::SameLine();\n            ImGui::Button(\"Some framed item\"); ImGui::SameLine();\n            HelpMarker(\"Baseline of button will look misaligned with text..\");\n\n            // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.\n            // (because we don't know what's coming after the Text() statement, we need to move the text baseline\n            // down by FramePadding.y ahead of time)\n            ImGui::AlignTextToFramePadding();\n            ImGui::Text(\"OK Blahblah\"); ImGui::SameLine();\n            ImGui::Button(\"Some framed item\"); ImGui::SameLine();\n            HelpMarker(\"We call AlignTextToFramePadding() to vertically align the text baseline by +FramePadding.y\");\n\n            // SmallButton() uses the same vertical padding as Text\n            ImGui::Button(\"TEST##1\"); ImGui::SameLine();\n            ImGui::Text(\"TEST\"); ImGui::SameLine();\n            ImGui::SmallButton(\"TEST##2\");\n\n            // If your line starts with text, call AlignTextToFramePadding() to align text to upcoming widgets.\n            ImGui::AlignTextToFramePadding();\n            ImGui::Text(\"Text aligned to framed item\"); ImGui::SameLine();\n            ImGui::Button(\"Item##1\"); ImGui::SameLine();\n            ImGui::Text(\"Item\"); ImGui::SameLine();\n            ImGui::SmallButton(\"Item##2\"); ImGui::SameLine();\n            ImGui::Button(\"Item##3\");\n\n            ImGui::Unindent();\n        }\n\n        ImGui::Spacing();\n\n        {\n            ImGui::BulletText(\"Multi-line text:\");\n            ImGui::Indent();\n            ImGui::Text(\"One\\nTwo\\nThree\"); ImGui::SameLine();\n            ImGui::Text(\"Hello\\nWorld\"); ImGui::SameLine();\n            ImGui::Text(\"Banana\");\n\n            ImGui::Text(\"Banana\"); ImGui::SameLine();\n            ImGui::Text(\"Hello\\nWorld\"); ImGui::SameLine();\n            ImGui::Text(\"One\\nTwo\\nThree\");\n\n            ImGui::Button(\"HOP##1\"); ImGui::SameLine();\n            ImGui::Text(\"Banana\"); ImGui::SameLine();\n            ImGui::Text(\"Hello\\nWorld\"); ImGui::SameLine();\n            ImGui::Text(\"Banana\");\n\n            ImGui::Button(\"HOP##2\"); ImGui::SameLine();\n            ImGui::Text(\"Hello\\nWorld\"); ImGui::SameLine();\n            ImGui::Text(\"Banana\");\n            ImGui::Unindent();\n        }\n\n        ImGui::Spacing();\n\n        {\n            ImGui::BulletText(\"Misc items:\");\n            ImGui::Indent();\n\n            // SmallButton() sets FramePadding to zero. Text baseline is aligned to match baseline of previous Button.\n            ImGui::Button(\"80x80\", ImVec2(80, 80));\n            ImGui::SameLine();\n            ImGui::Button(\"50x50\", ImVec2(50, 50));\n            ImGui::SameLine();\n            ImGui::Button(\"Button()\");\n            ImGui::SameLine();\n            ImGui::SmallButton(\"SmallButton()\");\n\n            // Tree\n            const float spacing = ImGui::GetStyle().ItemInnerSpacing.x;\n            ImGui::Button(\"Button##1\");\n            ImGui::SameLine(0.0f, spacing);\n            if (ImGui::TreeNode(\"Node##1\"))\n            {\n                // Placeholder tree data\n                for (int i = 0; i < 6; i++)\n                    ImGui::BulletText(\"Item %d..\", i);\n                ImGui::TreePop();\n            }\n\n            // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget.\n            // Otherwise you can use SmallButton() (smaller fit).\n            ImGui::AlignTextToFramePadding();\n\n            // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add\n            // other contents below the node.\n            bool node_open = ImGui::TreeNode(\"Node##2\");\n            ImGui::SameLine(0.0f, spacing); ImGui::Button(\"Button##2\");\n            if (node_open)\n            {\n                // Placeholder tree data\n                for (int i = 0; i < 6; i++)\n                    ImGui::BulletText(\"Item %d..\", i);\n                ImGui::TreePop();\n            }\n\n            // Bullet\n            ImGui::Button(\"Button##3\");\n            ImGui::SameLine(0.0f, spacing);\n            ImGui::BulletText(\"Bullet text\");\n\n            ImGui::AlignTextToFramePadding();\n            ImGui::BulletText(\"Node\");\n            ImGui::SameLine(0.0f, spacing); ImGui::Button(\"Button##4\");\n            ImGui::Unindent();\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Layout/Scrolling\");\n    if (ImGui::TreeNode(\"Scrolling\"))\n    {\n        // Vertical scroll functions\n        IMGUI_DEMO_MARKER(\"Layout/Scrolling/Vertical\");\n        HelpMarker(\"Use SetScrollHereY() or SetScrollFromPosY() to scroll to a given vertical position.\");\n\n        static int track_item = 50;\n        static bool enable_track = true;\n        static bool enable_extra_decorations = false;\n        static float scroll_to_off_px = 0.0f;\n        static float scroll_to_pos_px = 200.0f;\n\n        ImGui::Checkbox(\"Decoration\", &enable_extra_decorations);\n\n        ImGui::Checkbox(\"Track\", &enable_track);\n        ImGui::PushItemWidth(100);\n        ImGui::SameLine(140); enable_track |= ImGui::DragInt(\"##item\", &track_item, 0.25f, 0, 99, \"Item = %d\");\n\n        bool scroll_to_off = ImGui::Button(\"Scroll Offset\");\n        ImGui::SameLine(140); scroll_to_off |= ImGui::DragFloat(\"##off\", &scroll_to_off_px, 1.00f, 0, FLT_MAX, \"+%.0f px\");\n\n        bool scroll_to_pos = ImGui::Button(\"Scroll To Pos\");\n        ImGui::SameLine(140); scroll_to_pos |= ImGui::DragFloat(\"##pos\", &scroll_to_pos_px, 1.00f, -10, FLT_MAX, \"X/Y = %.0f px\");\n        ImGui::PopItemWidth();\n\n        if (scroll_to_off || scroll_to_pos)\n            enable_track = false;\n\n        ImGuiStyle& style = ImGui::GetStyle();\n        float child_w = (ImGui::GetContentRegionAvail().x - 4 * style.ItemSpacing.x) / 5;\n        if (child_w < 1.0f)\n            child_w = 1.0f;\n        ImGui::PushID(\"##VerticalScrolling\");\n        for (int i = 0; i < 5; i++)\n        {\n            if (i > 0) ImGui::SameLine();\n            ImGui::BeginGroup();\n            const char* names[] = { \"Top\", \"25%\", \"Center\", \"75%\", \"Bottom\" };\n            ImGui::TextUnformatted(names[i]);\n\n            const ImGuiWindowFlags child_flags = enable_extra_decorations ? ImGuiWindowFlags_MenuBar : 0;\n            const ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);\n            const bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(child_w, 200.0f), ImGuiChildFlags_Border, child_flags);\n            if (ImGui::BeginMenuBar())\n            {\n                ImGui::TextUnformatted(\"abc\");\n                ImGui::EndMenuBar();\n            }\n            if (scroll_to_off)\n                ImGui::SetScrollY(scroll_to_off_px);\n            if (scroll_to_pos)\n                ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_pos_px, i * 0.25f);\n            if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items\n            {\n                for (int item = 0; item < 100; item++)\n                {\n                    if (enable_track && item == track_item)\n                    {\n                        ImGui::TextColored(ImVec4(1, 1, 0, 1), \"Item %d\", item);\n                        ImGui::SetScrollHereY(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom\n                    }\n                    else\n                    {\n                        ImGui::Text(\"Item %d\", item);\n                    }\n                }\n            }\n            float scroll_y = ImGui::GetScrollY();\n            float scroll_max_y = ImGui::GetScrollMaxY();\n            ImGui::EndChild();\n            ImGui::Text(\"%.0f/%.0f\", scroll_y, scroll_max_y);\n            ImGui::EndGroup();\n        }\n        ImGui::PopID();\n\n        // Horizontal scroll functions\n        IMGUI_DEMO_MARKER(\"Layout/Scrolling/Horizontal\");\n        ImGui::Spacing();\n        HelpMarker(\n            \"Use SetScrollHereX() or SetScrollFromPosX() to scroll to a given horizontal position.\\n\\n\"\n            \"Because the clipping rectangle of most window hides half worth of WindowPadding on the \"\n            \"left/right, using SetScrollFromPosX(+1) will usually result in clipped text whereas the \"\n            \"equivalent SetScrollFromPosY(+1) wouldn't.\");\n        ImGui::PushID(\"##HorizontalScrolling\");\n        for (int i = 0; i < 5; i++)\n        {\n            float child_height = ImGui::GetTextLineHeight() + style.ScrollbarSize + style.WindowPadding.y * 2.0f;\n            ImGuiWindowFlags child_flags = ImGuiWindowFlags_HorizontalScrollbar | (enable_extra_decorations ? ImGuiWindowFlags_AlwaysVerticalScrollbar : 0);\n            ImGuiID child_id = ImGui::GetID((void*)(intptr_t)i);\n            bool child_is_visible = ImGui::BeginChild(child_id, ImVec2(-100, child_height), ImGuiChildFlags_Border, child_flags);\n            if (scroll_to_off)\n                ImGui::SetScrollX(scroll_to_off_px);\n            if (scroll_to_pos)\n                ImGui::SetScrollFromPosX(ImGui::GetCursorStartPos().x + scroll_to_pos_px, i * 0.25f);\n            if (child_is_visible) // Avoid calling SetScrollHereY when running with culled items\n            {\n                for (int item = 0; item < 100; item++)\n                {\n                    if (item > 0)\n                        ImGui::SameLine();\n                    if (enable_track && item == track_item)\n                    {\n                        ImGui::TextColored(ImVec4(1, 1, 0, 1), \"Item %d\", item);\n                        ImGui::SetScrollHereX(i * 0.25f); // 0.0f:left, 0.5f:center, 1.0f:right\n                    }\n                    else\n                    {\n                        ImGui::Text(\"Item %d\", item);\n                    }\n                }\n            }\n            float scroll_x = ImGui::GetScrollX();\n            float scroll_max_x = ImGui::GetScrollMaxX();\n            ImGui::EndChild();\n            ImGui::SameLine();\n            const char* names[] = { \"Left\", \"25%\", \"Center\", \"75%\", \"Right\" };\n            ImGui::Text(\"%s\\n%.0f/%.0f\", names[i], scroll_x, scroll_max_x);\n            ImGui::Spacing();\n        }\n        ImGui::PopID();\n\n        // Miscellaneous Horizontal Scrolling Demo\n        IMGUI_DEMO_MARKER(\"Layout/Scrolling/Horizontal (more)\");\n        HelpMarker(\n            \"Horizontal scrolling for a window is enabled via the ImGuiWindowFlags_HorizontalScrollbar flag.\\n\\n\"\n            \"You may want to also explicitly specify content width by using SetNextWindowContentWidth() before Begin().\");\n        static int lines = 7;\n        ImGui::SliderInt(\"Lines\", &lines, 1, 15);\n        ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);\n        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f));\n        ImVec2 scrolling_child_size = ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 7 + 30);\n        ImGui::BeginChild(\"scrolling\", scrolling_child_size, ImGuiChildFlags_Border, ImGuiWindowFlags_HorizontalScrollbar);\n        for (int line = 0; line < lines; line++)\n        {\n            // Display random stuff. For the sake of this trivial demo we are using basic Button() + SameLine()\n            // If you want to create your own time line for a real application you may be better off manipulating\n            // the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets\n            // yourself. You may also want to use the lower-level ImDrawList API.\n            int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3);\n            for (int n = 0; n < num_buttons; n++)\n            {\n                if (n > 0) ImGui::SameLine();\n                ImGui::PushID(n + line * 1000);\n                char num_buf[16];\n                sprintf(num_buf, \"%d\", n);\n                const char* label = (!(n % 15)) ? \"FizzBuzz\" : (!(n % 3)) ? \"Fizz\" : (!(n % 5)) ? \"Buzz\" : num_buf;\n                float hue = n * 0.05f;\n                ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f));\n                ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f));\n                ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f));\n                ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f));\n                ImGui::PopStyleColor(3);\n                ImGui::PopID();\n            }\n        }\n        float scroll_x = ImGui::GetScrollX();\n        float scroll_max_x = ImGui::GetScrollMaxX();\n        ImGui::EndChild();\n        ImGui::PopStyleVar(2);\n        float scroll_x_delta = 0.0f;\n        ImGui::SmallButton(\"<<\");\n        if (ImGui::IsItemActive())\n            scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f;\n        ImGui::SameLine();\n        ImGui::Text(\"Scroll from code\"); ImGui::SameLine();\n        ImGui::SmallButton(\">>\");\n        if (ImGui::IsItemActive())\n            scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f;\n        ImGui::SameLine();\n        ImGui::Text(\"%.0f/%.0f\", scroll_x, scroll_max_x);\n        if (scroll_x_delta != 0.0f)\n        {\n            // Demonstrate a trick: you can use Begin to set yourself in the context of another window\n            // (here we are already out of your child window)\n            ImGui::BeginChild(\"scrolling\");\n            ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta);\n            ImGui::EndChild();\n        }\n        ImGui::Spacing();\n\n        static bool show_horizontal_contents_size_demo_window = false;\n        ImGui::Checkbox(\"Show Horizontal contents size demo window\", &show_horizontal_contents_size_demo_window);\n\n        if (show_horizontal_contents_size_demo_window)\n        {\n            static bool show_h_scrollbar = true;\n            static bool show_button = true;\n            static bool show_tree_nodes = true;\n            static bool show_text_wrapped = false;\n            static bool show_columns = true;\n            static bool show_tab_bar = true;\n            static bool show_child = false;\n            static bool explicit_content_size = false;\n            static float contents_size_x = 300.0f;\n            if (explicit_content_size)\n                ImGui::SetNextWindowContentSize(ImVec2(contents_size_x, 0.0f));\n            ImGui::Begin(\"Horizontal contents size demo window\", &show_horizontal_contents_size_demo_window, show_h_scrollbar ? ImGuiWindowFlags_HorizontalScrollbar : 0);\n            IMGUI_DEMO_MARKER(\"Layout/Scrolling/Horizontal contents size demo window\");\n            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 0));\n            ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 0));\n            HelpMarker(\"Test of different widgets react and impact the work rectangle growing when horizontal scrolling is enabled.\\n\\nUse 'Metrics->Tools->Show windows rectangles' to visualize rectangles.\");\n            ImGui::Checkbox(\"H-scrollbar\", &show_h_scrollbar);\n            ImGui::Checkbox(\"Button\", &show_button);            // Will grow contents size (unless explicitly overwritten)\n            ImGui::Checkbox(\"Tree nodes\", &show_tree_nodes);    // Will grow contents size and display highlight over full width\n            ImGui::Checkbox(\"Text wrapped\", &show_text_wrapped);// Will grow and use contents size\n            ImGui::Checkbox(\"Columns\", &show_columns);          // Will use contents size\n            ImGui::Checkbox(\"Tab bar\", &show_tab_bar);          // Will use contents size\n            ImGui::Checkbox(\"Child\", &show_child);              // Will grow and use contents size\n            ImGui::Checkbox(\"Explicit content size\", &explicit_content_size);\n            ImGui::Text(\"Scroll %.1f/%.1f %.1f/%.1f\", ImGui::GetScrollX(), ImGui::GetScrollMaxX(), ImGui::GetScrollY(), ImGui::GetScrollMaxY());\n            if (explicit_content_size)\n            {\n                ImGui::SameLine();\n                ImGui::SetNextItemWidth(100);\n                ImGui::DragFloat(\"##csx\", &contents_size_x);\n                ImVec2 p = ImGui::GetCursorScreenPos();\n                ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 10, p.y + 10), IM_COL32_WHITE);\n                ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(p.x + contents_size_x - 10, p.y), ImVec2(p.x + contents_size_x, p.y + 10), IM_COL32_WHITE);\n                ImGui::Dummy(ImVec2(0, 10));\n            }\n            ImGui::PopStyleVar(2);\n            ImGui::Separator();\n            if (show_button)\n            {\n                ImGui::Button(\"this is a 300-wide button\", ImVec2(300, 0));\n            }\n            if (show_tree_nodes)\n            {\n                bool open = true;\n                if (ImGui::TreeNode(\"this is a tree node\"))\n                {\n                    if (ImGui::TreeNode(\"another one of those tree node...\"))\n                    {\n                        ImGui::Text(\"Some tree contents\");\n                        ImGui::TreePop();\n                    }\n                    ImGui::TreePop();\n                }\n                ImGui::CollapsingHeader(\"CollapsingHeader\", &open);\n            }\n            if (show_text_wrapped)\n            {\n                ImGui::TextWrapped(\"This text should automatically wrap on the edge of the work rectangle.\");\n            }\n            if (show_columns)\n            {\n                ImGui::Text(\"Tables:\");\n                if (ImGui::BeginTable(\"table\", 4, ImGuiTableFlags_Borders))\n                {\n                    for (int n = 0; n < 4; n++)\n                    {\n                        ImGui::TableNextColumn();\n                        ImGui::Text(\"Width %.2f\", ImGui::GetContentRegionAvail().x);\n                    }\n                    ImGui::EndTable();\n                }\n                ImGui::Text(\"Columns:\");\n                ImGui::Columns(4);\n                for (int n = 0; n < 4; n++)\n                {\n                    ImGui::Text(\"Width %.2f\", ImGui::GetColumnWidth());\n                    ImGui::NextColumn();\n                }\n                ImGui::Columns(1);\n            }\n            if (show_tab_bar && ImGui::BeginTabBar(\"Hello\"))\n            {\n                if (ImGui::BeginTabItem(\"OneOneOne\")) { ImGui::EndTabItem(); }\n                if (ImGui::BeginTabItem(\"TwoTwoTwo\")) { ImGui::EndTabItem(); }\n                if (ImGui::BeginTabItem(\"ThreeThreeThree\")) { ImGui::EndTabItem(); }\n                if (ImGui::BeginTabItem(\"FourFourFour\")) { ImGui::EndTabItem(); }\n                ImGui::EndTabBar();\n            }\n            if (show_child)\n            {\n                ImGui::BeginChild(\"child\", ImVec2(0, 0), ImGuiChildFlags_Border);\n                ImGui::EndChild();\n            }\n            ImGui::End();\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Layout/Clipping\");\n    if (ImGui::TreeNode(\"Clipping\"))\n    {\n        static ImVec2 size(100.0f, 100.0f);\n        static ImVec2 offset(30.0f, 30.0f);\n        ImGui::DragFloat2(\"size\", (float*)&size, 0.5f, 1.0f, 200.0f, \"%.0f\");\n        ImGui::TextWrapped(\"(Click and drag to scroll)\");\n\n        HelpMarker(\n            \"(Left) Using ImGui::PushClipRect():\\n\"\n            \"Will alter ImGui hit-testing logic + ImDrawList rendering.\\n\"\n            \"(use this if you want your clipping rectangle to affect interactions)\\n\\n\"\n            \"(Center) Using ImDrawList::PushClipRect():\\n\"\n            \"Will alter ImDrawList rendering only.\\n\"\n            \"(use this as a shortcut if you are only using ImDrawList calls)\\n\\n\"\n            \"(Right) Using ImDrawList::AddText() with a fine ClipRect:\\n\"\n            \"Will alter only this specific ImDrawList::AddText() rendering.\\n\"\n            \"This is often used internally to avoid altering the clipping rectangle and minimize draw calls.\");\n\n        for (int n = 0; n < 3; n++)\n        {\n            if (n > 0)\n                ImGui::SameLine();\n\n            ImGui::PushID(n);\n            ImGui::InvisibleButton(\"##canvas\", size);\n            if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left))\n            {\n                offset.x += ImGui::GetIO().MouseDelta.x;\n                offset.y += ImGui::GetIO().MouseDelta.y;\n            }\n            ImGui::PopID();\n            if (!ImGui::IsItemVisible()) // Skip rendering as ImDrawList elements are not clipped.\n                continue;\n\n            const ImVec2 p0 = ImGui::GetItemRectMin();\n            const ImVec2 p1 = ImGui::GetItemRectMax();\n            const char* text_str = \"Line 1 hello\\nLine 2 clip me!\";\n            const ImVec2 text_pos = ImVec2(p0.x + offset.x, p0.y + offset.y);\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n            switch (n)\n            {\n            case 0:\n                ImGui::PushClipRect(p0, p1, true);\n                draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));\n                draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);\n                ImGui::PopClipRect();\n                break;\n            case 1:\n                draw_list->PushClipRect(p0, p1, true);\n                draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));\n                draw_list->AddText(text_pos, IM_COL32_WHITE, text_str);\n                draw_list->PopClipRect();\n                break;\n            case 2:\n                ImVec4 clip_rect(p0.x, p0.y, p1.x, p1.y); // AddText() takes a ImVec4* here so let's convert.\n                draw_list->AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255));\n                draw_list->AddText(ImGui::GetFont(), ImGui::GetFontSize(), text_pos, IM_COL32_WHITE, text_str, NULL, 0.0f, &clip_rect);\n                break;\n            }\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Layout/Overlap Mode\");\n    if (ImGui::TreeNode(\"Overlap Mode\"))\n    {\n        static bool enable_allow_overlap = true;\n\n        HelpMarker(\n            \"Hit-testing is by default performed in item submission order, which generally is perceived as 'back-to-front'.\\n\\n\"\n            \"By using SetNextItemAllowOverlap() you can notify that an item may be overlapped by another. Doing so alters the hovering logic: items using AllowOverlap mode requires an extra frame to accept hovered state.\");\n        ImGui::Checkbox(\"Enable AllowOverlap\", &enable_allow_overlap);\n\n        ImVec2 button1_pos = ImGui::GetCursorScreenPos();\n        ImVec2 button2_pos = ImVec2(button1_pos.x + 50.0f, button1_pos.y + 50.0f);\n        if (enable_allow_overlap)\n            ImGui::SetNextItemAllowOverlap();\n        ImGui::Button(\"Button 1\", ImVec2(80, 80));\n        ImGui::SetCursorScreenPos(button2_pos);\n        ImGui::Button(\"Button 2\", ImVec2(80, 80));\n\n        // This is typically used with width-spanning items.\n        // (note that Selectable() has a dedicated flag ImGuiSelectableFlags_AllowOverlap, which is a shortcut\n        // for using SetNextItemAllowOverlap(). For demo purpose we use SetNextItemAllowOverlap() here.)\n        if (enable_allow_overlap)\n            ImGui::SetNextItemAllowOverlap();\n        ImGui::Selectable(\"Some Selectable\", false);\n        ImGui::SameLine();\n        ImGui::SmallButton(\"++\");\n\n        ImGui::TreePop();\n    }\n}\n\nstatic void ShowDemoWindowPopups()\n{\n    IMGUI_DEMO_MARKER(\"Popups\");\n    if (!ImGui::CollapsingHeader(\"Popups & Modal windows\"))\n        return;\n\n    // The properties of popups windows are:\n    // - They block normal mouse hovering detection outside them. (*)\n    // - Unless modal, they can be closed by clicking anywhere outside them, or by pressing ESCAPE.\n    // - Their visibility state (~bool) is held internally by Dear ImGui instead of being held by the programmer as\n    //   we are used to with regular Begin() calls. User can manipulate the visibility state by calling OpenPopup().\n    // (*) One can use IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) to bypass it and detect hovering even\n    //     when normally blocked by a popup.\n    // Those three properties are connected. The library needs to hold their visibility state BECAUSE it can close\n    // popups at any time.\n\n    // Typical use for regular windows:\n    //   bool my_tool_is_active = false; if (ImGui::Button(\"Open\")) my_tool_is_active = true; [...] if (my_tool_is_active) Begin(\"My Tool\", &my_tool_is_active) { [...] } End();\n    // Typical use for popups:\n    //   if (ImGui::Button(\"Open\")) ImGui::OpenPopup(\"MyPopup\"); if (ImGui::BeginPopup(\"MyPopup\") { [...] EndPopup(); }\n\n    // With popups we have to go through a library call (here OpenPopup) to manipulate the visibility state.\n    // This may be a bit confusing at first but it should quickly make sense. Follow on the examples below.\n\n    IMGUI_DEMO_MARKER(\"Popups/Popups\");\n    if (ImGui::TreeNode(\"Popups\"))\n    {\n        ImGui::TextWrapped(\n            \"When a popup is active, it inhibits interacting with windows that are behind the popup. \"\n            \"Clicking outside the popup closes it.\");\n\n        static int selected_fish = -1;\n        const char* names[] = { \"Bream\", \"Haddock\", \"Mackerel\", \"Pollock\", \"Tilefish\" };\n        static bool toggles[] = { true, false, false, false, false };\n\n        // Simple selection popup (if you want to show the current selection inside the Button itself,\n        // you may want to build a string using the \"###\" operator to preserve a constant ID with a variable label)\n        if (ImGui::Button(\"Select..\"))\n            ImGui::OpenPopup(\"my_select_popup\");\n        ImGui::SameLine();\n        ImGui::TextUnformatted(selected_fish == -1 ? \"<None>\" : names[selected_fish]);\n        if (ImGui::BeginPopup(\"my_select_popup\"))\n        {\n            ImGui::SeparatorText(\"Aquarium\");\n            for (int i = 0; i < IM_ARRAYSIZE(names); i++)\n                if (ImGui::Selectable(names[i]))\n                    selected_fish = i;\n            ImGui::EndPopup();\n        }\n\n        // Showing a menu with toggles\n        if (ImGui::Button(\"Toggle..\"))\n            ImGui::OpenPopup(\"my_toggle_popup\");\n        if (ImGui::BeginPopup(\"my_toggle_popup\"))\n        {\n            for (int i = 0; i < IM_ARRAYSIZE(names); i++)\n                ImGui::MenuItem(names[i], \"\", &toggles[i]);\n            if (ImGui::BeginMenu(\"Sub-menu\"))\n            {\n                ImGui::MenuItem(\"Click me\");\n                ImGui::EndMenu();\n            }\n\n            ImGui::Separator();\n            ImGui::Text(\"Tooltip here\");\n            ImGui::SetItemTooltip(\"I am a tooltip over a popup\");\n\n            if (ImGui::Button(\"Stacked Popup\"))\n                ImGui::OpenPopup(\"another popup\");\n            if (ImGui::BeginPopup(\"another popup\"))\n            {\n                for (int i = 0; i < IM_ARRAYSIZE(names); i++)\n                    ImGui::MenuItem(names[i], \"\", &toggles[i]);\n                if (ImGui::BeginMenu(\"Sub-menu\"))\n                {\n                    ImGui::MenuItem(\"Click me\");\n                    if (ImGui::Button(\"Stacked Popup\"))\n                        ImGui::OpenPopup(\"another popup\");\n                    if (ImGui::BeginPopup(\"another popup\"))\n                    {\n                        ImGui::Text(\"I am the last one here.\");\n                        ImGui::EndPopup();\n                    }\n                    ImGui::EndMenu();\n                }\n                ImGui::EndPopup();\n            }\n            ImGui::EndPopup();\n        }\n\n        // Call the more complete ShowExampleMenuFile which we use in various places of this demo\n        if (ImGui::Button(\"With a menu..\"))\n            ImGui::OpenPopup(\"my_file_popup\");\n        if (ImGui::BeginPopup(\"my_file_popup\", ImGuiWindowFlags_MenuBar))\n        {\n            if (ImGui::BeginMenuBar())\n            {\n                if (ImGui::BeginMenu(\"File\"))\n                {\n                    ShowExampleMenuFile();\n                    ImGui::EndMenu();\n                }\n                if (ImGui::BeginMenu(\"Edit\"))\n                {\n                    ImGui::MenuItem(\"Dummy\");\n                    ImGui::EndMenu();\n                }\n                ImGui::EndMenuBar();\n            }\n            ImGui::Text(\"Hello from popup!\");\n            ImGui::Button(\"This is a dummy button..\");\n            ImGui::EndPopup();\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Popups/Context menus\");\n    if (ImGui::TreeNode(\"Context menus\"))\n    {\n        HelpMarker(\"\\\"Context\\\" functions are simple helpers to associate a Popup to a given Item or Window identifier.\");\n\n        // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing:\n        //     if (id == 0)\n        //         id = GetItemID(); // Use last item id\n        //     if (IsItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))\n        //         OpenPopup(id);\n        //     return BeginPopup(id);\n        // For advanced uses you may want to replicate and customize this code.\n        // See more details in BeginPopupContextItem().\n\n        // Example 1\n        // When used after an item that has an ID (e.g. Button), we can skip providing an ID to BeginPopupContextItem(),\n        // and BeginPopupContextItem() will use the last item ID as the popup ID.\n        {\n            const char* names[5] = { \"Label1\", \"Label2\", \"Label3\", \"Label4\", \"Label5\" };\n            static int selected = -1;\n            for (int n = 0; n < 5; n++)\n            {\n                if (ImGui::Selectable(names[n], selected == n))\n                    selected = n;\n                if (ImGui::BeginPopupContextItem()) // <-- use last item id as popup id\n                {\n                    selected = n;\n                    ImGui::Text(\"This a popup for \\\"%s\\\"!\", names[n]);\n                    if (ImGui::Button(\"Close\"))\n                        ImGui::CloseCurrentPopup();\n                    ImGui::EndPopup();\n                }\n                ImGui::SetItemTooltip(\"Right-click to open popup\");\n            }\n        }\n\n        // Example 2\n        // Popup on a Text() element which doesn't have an identifier: we need to provide an identifier to BeginPopupContextItem().\n        // Using an explicit identifier is also convenient if you want to activate the popups from different locations.\n        {\n            HelpMarker(\"Text() elements don't have stable identifiers so we need to provide one.\");\n            static float value = 0.5f;\n            ImGui::Text(\"Value = %.3f <-- (1) right-click this text\", value);\n            if (ImGui::BeginPopupContextItem(\"my popup\"))\n            {\n                if (ImGui::Selectable(\"Set to zero\")) value = 0.0f;\n                if (ImGui::Selectable(\"Set to PI\")) value = 3.1415f;\n                ImGui::SetNextItemWidth(-FLT_MIN);\n                ImGui::DragFloat(\"##Value\", &value, 0.1f, 0.0f, 0.0f);\n                ImGui::EndPopup();\n            }\n\n            // We can also use OpenPopupOnItemClick() to toggle the visibility of a given popup.\n            // Here we make it that right-clicking this other text element opens the same popup as above.\n            // The popup itself will be submitted by the code above.\n            ImGui::Text(\"(2) Or right-click this text\");\n            ImGui::OpenPopupOnItemClick(\"my popup\", ImGuiPopupFlags_MouseButtonRight);\n\n            // Back to square one: manually open the same popup.\n            if (ImGui::Button(\"(3) Or click this button\"))\n                ImGui::OpenPopup(\"my popup\");\n        }\n\n        // Example 3\n        // When using BeginPopupContextItem() with an implicit identifier (NULL == use last item ID),\n        // we need to make sure your item identifier is stable.\n        // In this example we showcase altering the item label while preserving its identifier, using the ### operator (see FAQ).\n        {\n            HelpMarker(\"Showcase using a popup ID linked to item ID, with the item having a changing label + stable ID using the ### operator.\");\n            static char name[32] = \"Label1\";\n            char buf[64];\n            sprintf(buf, \"Button: %s###Button\", name); // ### operator override ID ignoring the preceding label\n            ImGui::Button(buf);\n            if (ImGui::BeginPopupContextItem())\n            {\n                ImGui::Text(\"Edit name:\");\n                ImGui::InputText(\"##edit\", name, IM_ARRAYSIZE(name));\n                if (ImGui::Button(\"Close\"))\n                    ImGui::CloseCurrentPopup();\n                ImGui::EndPopup();\n            }\n            ImGui::SameLine(); ImGui::Text(\"(<-- right-click here)\");\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Popups/Modals\");\n    if (ImGui::TreeNode(\"Modals\"))\n    {\n        ImGui::TextWrapped(\"Modal windows are like popups but the user cannot close them by clicking outside.\");\n\n        if (ImGui::Button(\"Delete..\"))\n            ImGui::OpenPopup(\"Delete?\");\n\n        // Always center this window when appearing\n        ImVec2 center = ImGui::GetMainViewport()->GetCenter();\n        ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));\n\n        if (ImGui::BeginPopupModal(\"Delete?\", NULL, ImGuiWindowFlags_AlwaysAutoResize))\n        {\n            ImGui::Text(\"All those beautiful files will be deleted.\\nThis operation cannot be undone!\");\n            ImGui::Separator();\n\n            //static int unused_i = 0;\n            //ImGui::Combo(\"Combo\", &unused_i, \"Delete\\0Delete harder\\0\");\n\n            static bool dont_ask_me_next_time = false;\n            ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));\n            ImGui::Checkbox(\"Don't ask me next time\", &dont_ask_me_next_time);\n            ImGui::PopStyleVar();\n\n            if (ImGui::Button(\"OK\", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }\n            ImGui::SetItemDefaultFocus();\n            ImGui::SameLine();\n            if (ImGui::Button(\"Cancel\", ImVec2(120, 0))) { ImGui::CloseCurrentPopup(); }\n            ImGui::EndPopup();\n        }\n\n        if (ImGui::Button(\"Stacked modals..\"))\n            ImGui::OpenPopup(\"Stacked 1\");\n        if (ImGui::BeginPopupModal(\"Stacked 1\", NULL, ImGuiWindowFlags_MenuBar))\n        {\n            if (ImGui::BeginMenuBar())\n            {\n                if (ImGui::BeginMenu(\"File\"))\n                {\n                    if (ImGui::MenuItem(\"Some menu item\")) {}\n                    ImGui::EndMenu();\n                }\n                ImGui::EndMenuBar();\n            }\n            ImGui::Text(\"Hello from Stacked The First\\nUsing style.Colors[ImGuiCol_ModalWindowDimBg] behind it.\");\n\n            // Testing behavior of widgets stacking their own regular popups over the modal.\n            static int item = 1;\n            static float color[4] = { 0.4f, 0.7f, 0.0f, 0.5f };\n            ImGui::Combo(\"Combo\", &item, \"aaaa\\0bbbb\\0cccc\\0dddd\\0eeee\\0\\0\");\n            ImGui::ColorEdit4(\"color\", color);\n\n            if (ImGui::Button(\"Add another modal..\"))\n                ImGui::OpenPopup(\"Stacked 2\");\n\n            // Also demonstrate passing a bool* to BeginPopupModal(), this will create a regular close button which\n            // will close the popup. Note that the visibility state of popups is owned by imgui, so the input value\n            // of the bool actually doesn't matter here.\n            bool unused_open = true;\n            if (ImGui::BeginPopupModal(\"Stacked 2\", &unused_open))\n            {\n                ImGui::Text(\"Hello from Stacked The Second!\");\n                if (ImGui::Button(\"Close\"))\n                    ImGui::CloseCurrentPopup();\n                ImGui::EndPopup();\n            }\n\n            if (ImGui::Button(\"Close\"))\n                ImGui::CloseCurrentPopup();\n            ImGui::EndPopup();\n        }\n\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Popups/Menus inside a regular window\");\n    if (ImGui::TreeNode(\"Menus inside a regular window\"))\n    {\n        ImGui::TextWrapped(\"Below we are testing adding menu items to a regular window. It's rather unusual but should work!\");\n        ImGui::Separator();\n\n        ImGui::MenuItem(\"Menu item\", \"CTRL+M\");\n        if (ImGui::BeginMenu(\"Menu inside a regular window\"))\n        {\n            ShowExampleMenuFile();\n            ImGui::EndMenu();\n        }\n        ImGui::Separator();\n        ImGui::TreePop();\n    }\n}\n\n// Dummy data structure that we use for the Table demo.\n// (pre-C++11 doesn't allow us to instantiate ImVector<MyItem> template if this structure is defined inside the demo function)\nnamespace\n{\n// We are passing our own identifier to TableSetupColumn() to facilitate identifying columns in the sorting code.\n// This identifier will be passed down into ImGuiTableSortSpec::ColumnUserID.\n// But it is possible to omit the user id parameter of TableSetupColumn() and just use the column index instead! (ImGuiTableSortSpec::ColumnIndex)\n// If you don't use sorting, you will generally never care about giving column an ID!\nenum MyItemColumnID\n{\n    MyItemColumnID_ID,\n    MyItemColumnID_Name,\n    MyItemColumnID_Action,\n    MyItemColumnID_Quantity,\n    MyItemColumnID_Description\n};\n\nstruct MyItem\n{\n    int         ID;\n    const char* Name;\n    int         Quantity;\n\n    // We have a problem which is affecting _only this demo_ and should not affect your code:\n    // As we don't rely on std:: or other third-party library to compile dear imgui, we only have reliable access to qsort(),\n    // however qsort doesn't allow passing user data to comparing function.\n    // As a workaround, we are storing the sort specs in a static/global for the comparing function to access.\n    // In your own use case you would probably pass the sort specs to your sorting/comparing functions directly and not use a global.\n    // We could technically call ImGui::TableGetSortSpecs() in CompareWithSortSpecs(), but considering that this function is called\n    // very often by the sorting algorithm it would be a little wasteful.\n    static const ImGuiTableSortSpecs* s_current_sort_specs;\n\n    static void SortWithSortSpecs(ImGuiTableSortSpecs* sort_specs, MyItem* items, int items_count)\n    {\n        s_current_sort_specs = sort_specs; // Store in variable accessible by the sort function.\n        if (items_count > 1)\n            qsort(items, (size_t)items_count, sizeof(items[0]), MyItem::CompareWithSortSpecs);\n        s_current_sort_specs = NULL;\n    }\n\n    // Compare function to be used by qsort()\n    static int IMGUI_CDECL CompareWithSortSpecs(const void* lhs, const void* rhs)\n    {\n        const MyItem* a = (const MyItem*)lhs;\n        const MyItem* b = (const MyItem*)rhs;\n        for (int n = 0; n < s_current_sort_specs->SpecsCount; n++)\n        {\n            // Here we identify columns using the ColumnUserID value that we ourselves passed to TableSetupColumn()\n            // We could also choose to identify columns based on their index (sort_spec->ColumnIndex), which is simpler!\n            const ImGuiTableColumnSortSpecs* sort_spec = &s_current_sort_specs->Specs[n];\n            int delta = 0;\n            switch (sort_spec->ColumnUserID)\n            {\n            case MyItemColumnID_ID:             delta = (a->ID - b->ID);                break;\n            case MyItemColumnID_Name:           delta = (strcmp(a->Name, b->Name));     break;\n            case MyItemColumnID_Quantity:       delta = (a->Quantity - b->Quantity);    break;\n            case MyItemColumnID_Description:    delta = (strcmp(a->Name, b->Name));     break;\n            default: IM_ASSERT(0); break;\n            }\n            if (delta > 0)\n                return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? +1 : -1;\n            if (delta < 0)\n                return (sort_spec->SortDirection == ImGuiSortDirection_Ascending) ? -1 : +1;\n        }\n\n        // qsort() is instable so always return a way to differenciate items.\n        // Your own compare function may want to avoid fallback on implicit sort specs e.g. a Name compare if it wasn't already part of the sort specs.\n        return (a->ID - b->ID);\n    }\n};\nconst ImGuiTableSortSpecs* MyItem::s_current_sort_specs = NULL;\n}\n\n// Make the UI compact because there are so many fields\nstatic void PushStyleCompact()\n{\n    ImGuiStyle& style = ImGui::GetStyle();\n    ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, (float)(int)(style.FramePadding.y * 0.60f)));\n    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x, (float)(int)(style.ItemSpacing.y * 0.60f)));\n}\n\nstatic void PopStyleCompact()\n{\n    ImGui::PopStyleVar(2);\n}\n\n// Show a combo box with a choice of sizing policies\nstatic void EditTableSizingFlags(ImGuiTableFlags* p_flags)\n{\n    struct EnumDesc { ImGuiTableFlags Value; const char* Name; const char* Tooltip; };\n    static const EnumDesc policies[] =\n    {\n        { ImGuiTableFlags_None,               \"Default\",                            \"Use default sizing policy:\\n- ImGuiTableFlags_SizingFixedFit if ScrollX is on or if host window has ImGuiWindowFlags_AlwaysAutoResize.\\n- ImGuiTableFlags_SizingStretchSame otherwise.\" },\n        { ImGuiTableFlags_SizingFixedFit,     \"ImGuiTableFlags_SizingFixedFit\",     \"Columns default to _WidthFixed (if resizable) or _WidthAuto (if not resizable), matching contents width.\" },\n        { ImGuiTableFlags_SizingFixedSame,    \"ImGuiTableFlags_SizingFixedSame\",    \"Columns are all the same width, matching the maximum contents width.\\nImplicitly disable ImGuiTableFlags_Resizable and enable ImGuiTableFlags_NoKeepColumnsVisible.\" },\n        { ImGuiTableFlags_SizingStretchProp,  \"ImGuiTableFlags_SizingStretchProp\",  \"Columns default to _WidthStretch with weights proportional to their widths.\" },\n        { ImGuiTableFlags_SizingStretchSame,  \"ImGuiTableFlags_SizingStretchSame\",  \"Columns default to _WidthStretch with same weights.\" }\n    };\n    int idx;\n    for (idx = 0; idx < IM_ARRAYSIZE(policies); idx++)\n        if (policies[idx].Value == (*p_flags & ImGuiTableFlags_SizingMask_))\n            break;\n    const char* preview_text = (idx < IM_ARRAYSIZE(policies)) ? policies[idx].Name + (idx > 0 ? strlen(\"ImGuiTableFlags\") : 0) : \"\";\n    if (ImGui::BeginCombo(\"Sizing Policy\", preview_text))\n    {\n        for (int n = 0; n < IM_ARRAYSIZE(policies); n++)\n            if (ImGui::Selectable(policies[n].Name, idx == n))\n                *p_flags = (*p_flags & ~ImGuiTableFlags_SizingMask_) | policies[n].Value;\n        ImGui::EndCombo();\n    }\n    ImGui::SameLine();\n    ImGui::TextDisabled(\"(?)\");\n    if (ImGui::BeginItemTooltip())\n    {\n        ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f);\n        for (int m = 0; m < IM_ARRAYSIZE(policies); m++)\n        {\n            ImGui::Separator();\n            ImGui::Text(\"%s:\", policies[m].Name);\n            ImGui::Separator();\n            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetStyle().IndentSpacing * 0.5f);\n            ImGui::TextUnformatted(policies[m].Tooltip);\n        }\n        ImGui::PopTextWrapPos();\n        ImGui::EndTooltip();\n    }\n}\n\nstatic void EditTableColumnsFlags(ImGuiTableColumnFlags* p_flags)\n{\n    ImGui::CheckboxFlags(\"_Disabled\", p_flags, ImGuiTableColumnFlags_Disabled); ImGui::SameLine(); HelpMarker(\"Master disable flag (also hide from context menu)\");\n    ImGui::CheckboxFlags(\"_DefaultHide\", p_flags, ImGuiTableColumnFlags_DefaultHide);\n    ImGui::CheckboxFlags(\"_DefaultSort\", p_flags, ImGuiTableColumnFlags_DefaultSort);\n    if (ImGui::CheckboxFlags(\"_WidthStretch\", p_flags, ImGuiTableColumnFlags_WidthStretch))\n        *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthStretch);\n    if (ImGui::CheckboxFlags(\"_WidthFixed\", p_flags, ImGuiTableColumnFlags_WidthFixed))\n        *p_flags &= ~(ImGuiTableColumnFlags_WidthMask_ ^ ImGuiTableColumnFlags_WidthFixed);\n    ImGui::CheckboxFlags(\"_NoResize\", p_flags, ImGuiTableColumnFlags_NoResize);\n    ImGui::CheckboxFlags(\"_NoReorder\", p_flags, ImGuiTableColumnFlags_NoReorder);\n    ImGui::CheckboxFlags(\"_NoHide\", p_flags, ImGuiTableColumnFlags_NoHide);\n    ImGui::CheckboxFlags(\"_NoClip\", p_flags, ImGuiTableColumnFlags_NoClip);\n    ImGui::CheckboxFlags(\"_NoSort\", p_flags, ImGuiTableColumnFlags_NoSort);\n    ImGui::CheckboxFlags(\"_NoSortAscending\", p_flags, ImGuiTableColumnFlags_NoSortAscending);\n    ImGui::CheckboxFlags(\"_NoSortDescending\", p_flags, ImGuiTableColumnFlags_NoSortDescending);\n    ImGui::CheckboxFlags(\"_NoHeaderLabel\", p_flags, ImGuiTableColumnFlags_NoHeaderLabel);\n    ImGui::CheckboxFlags(\"_NoHeaderWidth\", p_flags, ImGuiTableColumnFlags_NoHeaderWidth);\n    ImGui::CheckboxFlags(\"_PreferSortAscending\", p_flags, ImGuiTableColumnFlags_PreferSortAscending);\n    ImGui::CheckboxFlags(\"_PreferSortDescending\", p_flags, ImGuiTableColumnFlags_PreferSortDescending);\n    ImGui::CheckboxFlags(\"_IndentEnable\", p_flags, ImGuiTableColumnFlags_IndentEnable); ImGui::SameLine(); HelpMarker(\"Default for column 0\");\n    ImGui::CheckboxFlags(\"_IndentDisable\", p_flags, ImGuiTableColumnFlags_IndentDisable); ImGui::SameLine(); HelpMarker(\"Default for column >0\");\n    ImGui::CheckboxFlags(\"_AngledHeader\", p_flags, ImGuiTableColumnFlags_AngledHeader);\n}\n\nstatic void ShowTableColumnsStatusFlags(ImGuiTableColumnFlags flags)\n{\n    ImGui::CheckboxFlags(\"_IsEnabled\", &flags, ImGuiTableColumnFlags_IsEnabled);\n    ImGui::CheckboxFlags(\"_IsVisible\", &flags, ImGuiTableColumnFlags_IsVisible);\n    ImGui::CheckboxFlags(\"_IsSorted\", &flags, ImGuiTableColumnFlags_IsSorted);\n    ImGui::CheckboxFlags(\"_IsHovered\", &flags, ImGuiTableColumnFlags_IsHovered);\n}\n\nstatic void ShowDemoWindowTables()\n{\n    //ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n    IMGUI_DEMO_MARKER(\"Tables\");\n    if (!ImGui::CollapsingHeader(\"Tables & Columns\"))\n        return;\n\n    // Using those as a base value to create width/height that are factor of the size of our font\n    const float TEXT_BASE_WIDTH = ImGui::CalcTextSize(\"A\").x;\n    const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();\n\n    ImGui::PushID(\"Tables\");\n\n    int open_action = -1;\n    if (ImGui::Button(\"Expand all\"))\n        open_action = 1;\n    ImGui::SameLine();\n    if (ImGui::Button(\"Collapse all\"))\n        open_action = 0;\n    ImGui::SameLine();\n\n    // Options\n    static bool disable_indent = false;\n    ImGui::Checkbox(\"Disable tree indentation\", &disable_indent);\n    ImGui::SameLine();\n    HelpMarker(\"Disable the indenting of tree nodes so demo tables can use the full window width.\");\n    ImGui::Separator();\n    if (disable_indent)\n        ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0.0f);\n\n    // About Styling of tables\n    // Most settings are configured on a per-table basis via the flags passed to BeginTable() and TableSetupColumns APIs.\n    // There are however a few settings that a shared and part of the ImGuiStyle structure:\n    //   style.CellPadding                          // Padding within each cell\n    //   style.Colors[ImGuiCol_TableHeaderBg]       // Table header background\n    //   style.Colors[ImGuiCol_TableBorderStrong]   // Table outer and header borders\n    //   style.Colors[ImGuiCol_TableBorderLight]    // Table inner borders\n    //   style.Colors[ImGuiCol_TableRowBg]          // Table row background when ImGuiTableFlags_RowBg is enabled (even rows)\n    //   style.Colors[ImGuiCol_TableRowBgAlt]       // Table row background when ImGuiTableFlags_RowBg is enabled (odds rows)\n\n    // Demos\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Basic\");\n    if (ImGui::TreeNode(\"Basic\"))\n    {\n        // Here we will showcase three different ways to output a table.\n        // They are very simple variations of a same thing!\n\n        // [Method 1] Using TableNextRow() to create a new row, and TableSetColumnIndex() to select the column.\n        // In many situations, this is the most flexible and easy to use pattern.\n        HelpMarker(\"Using TableNextRow() + calling TableSetColumnIndex() _before_ each cell, in a loop.\");\n        if (ImGui::BeginTable(\"table1\", 3))\n        {\n            for (int row = 0; row < 4; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Row %d Column %d\", row, column);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        // [Method 2] Using TableNextColumn() called multiple times, instead of using a for loop + TableSetColumnIndex().\n        // This is generally more convenient when you have code manually submitting the contents of each column.\n        HelpMarker(\"Using TableNextRow() + calling TableNextColumn() _before_ each cell, manually.\");\n        if (ImGui::BeginTable(\"table2\", 3))\n        {\n            for (int row = 0; row < 4; row++)\n            {\n                ImGui::TableNextRow();\n                ImGui::TableNextColumn();\n                ImGui::Text(\"Row %d\", row);\n                ImGui::TableNextColumn();\n                ImGui::Text(\"Some contents\");\n                ImGui::TableNextColumn();\n                ImGui::Text(\"123.456\");\n            }\n            ImGui::EndTable();\n        }\n\n        // [Method 3] We call TableNextColumn() _before_ each cell. We never call TableNextRow(),\n        // as TableNextColumn() will automatically wrap around and create new rows as needed.\n        // This is generally more convenient when your cells all contains the same type of data.\n        HelpMarker(\n            \"Only using TableNextColumn(), which tends to be convenient for tables where every cell contains the same type of contents.\\n\"\n            \"This is also more similar to the old NextColumn() function of the Columns API, and provided to facilitate the Columns->Tables API transition.\");\n        if (ImGui::BeginTable(\"table3\", 3))\n        {\n            for (int item = 0; item < 14; item++)\n            {\n                ImGui::TableNextColumn();\n                ImGui::Text(\"Item %d\", item);\n            }\n            ImGui::EndTable();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Borders, background\");\n    if (ImGui::TreeNode(\"Borders, background\"))\n    {\n        // Expose a few Borders related flags interactively\n        enum ContentsType { CT_Text, CT_FillButton };\n        static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;\n        static bool display_headers = false;\n        static int contents_type = CT_Text;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_RowBg\", &flags, ImGuiTableFlags_RowBg);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Borders\", &flags, ImGuiTableFlags_Borders);\n        ImGui::SameLine(); HelpMarker(\"ImGuiTableFlags_Borders\\n = ImGuiTableFlags_BordersInnerV\\n | ImGuiTableFlags_BordersOuterV\\n | ImGuiTableFlags_BordersInnerV\\n | ImGuiTableFlags_BordersOuterH\");\n        ImGui::Indent();\n\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersH\", &flags, ImGuiTableFlags_BordersH);\n        ImGui::Indent();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterH\", &flags, ImGuiTableFlags_BordersOuterH);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerH\", &flags, ImGuiTableFlags_BordersInnerH);\n        ImGui::Unindent();\n\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersV\", &flags, ImGuiTableFlags_BordersV);\n        ImGui::Indent();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterV\", &flags, ImGuiTableFlags_BordersOuterV);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerV\", &flags, ImGuiTableFlags_BordersInnerV);\n        ImGui::Unindent();\n\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuter\", &flags, ImGuiTableFlags_BordersOuter);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInner\", &flags, ImGuiTableFlags_BordersInner);\n        ImGui::Unindent();\n\n        ImGui::AlignTextToFramePadding(); ImGui::Text(\"Cell contents:\");\n        ImGui::SameLine(); ImGui::RadioButton(\"Text\", &contents_type, CT_Text);\n        ImGui::SameLine(); ImGui::RadioButton(\"FillButton\", &contents_type, CT_FillButton);\n        ImGui::Checkbox(\"Display headers\", &display_headers);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBody\", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker(\"Disable vertical borders in columns Body (borders will always appear in Headers\");\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table1\", 3, flags))\n        {\n            // Display headers so we can inspect their interaction with borders.\n            // (Headers are not the main purpose of this section of the demo, so we are not elaborating on them too much. See other sections for details)\n            if (display_headers)\n            {\n                ImGui::TableSetupColumn(\"One\");\n                ImGui::TableSetupColumn(\"Two\");\n                ImGui::TableSetupColumn(\"Three\");\n                ImGui::TableHeadersRow();\n            }\n\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    char buf[32];\n                    sprintf(buf, \"Hello %d,%d\", column, row);\n                    if (contents_type == CT_Text)\n                        ImGui::TextUnformatted(buf);\n                    else if (contents_type == CT_FillButton)\n                        ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Resizable, stretch\");\n    if (ImGui::TreeNode(\"Resizable, stretch\"))\n    {\n        // By default, if we don't enable ScrollX the sizing policy for each column is \"Stretch\"\n        // All columns maintain a sizing weight, and they will occupy all available width.\n        static ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersV\", &flags, ImGuiTableFlags_BordersV);\n        ImGui::SameLine(); HelpMarker(\"Using the _Resizable flag automatically enables the _BordersInnerV flag as well, this is why the resize borders are still showing when unchecking this.\");\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table1\", 3, flags))\n        {\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Hello %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Resizable, fixed\");\n    if (ImGui::TreeNode(\"Resizable, fixed\"))\n    {\n        // Here we use ImGuiTableFlags_SizingFixedFit (even though _ScrollX is not set)\n        // So columns will adopt the \"Fixed\" policy and will maintain a fixed width regardless of the whole available width (unless table is small)\n        // If there is not enough available width to fit all columns, they will however be resized down.\n        // FIXME-TABLE: Providing a stretch-on-init would make sense especially for tables which don't have saved settings\n        HelpMarker(\n            \"Using _Resizable + _SizingFixedFit flags.\\n\"\n            \"Fixed-width columns generally makes more sense if you want to use horizontal scrolling.\\n\\n\"\n            \"Double-click a column border to auto-fit the column to its contents.\");\n        PushStyleCompact();\n        static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendX\", &flags, ImGuiTableFlags_NoHostExtendX);\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table1\", 3, flags))\n        {\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Hello %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Resizable, mixed\");\n    if (ImGui::TreeNode(\"Resizable, mixed\"))\n    {\n        HelpMarker(\n            \"Using TableSetupColumn() to alter resizing policy on a per-column basis.\\n\\n\"\n            \"When combining Fixed and Stretch columns, generally you only want one, maybe two trailing columns to use _WidthStretch.\");\n        static ImGuiTableFlags flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;\n\n        if (ImGui::BeginTable(\"table1\", 3, flags))\n        {\n            ImGui::TableSetupColumn(\"AAA\", ImGuiTableColumnFlags_WidthFixed);\n            ImGui::TableSetupColumn(\"BBB\", ImGuiTableColumnFlags_WidthFixed);\n            ImGui::TableSetupColumn(\"CCC\", ImGuiTableColumnFlags_WidthStretch);\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"%s %d,%d\", (column == 2) ? \"Stretch\" : \"Fixed\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        if (ImGui::BeginTable(\"table2\", 6, flags))\n        {\n            ImGui::TableSetupColumn(\"AAA\", ImGuiTableColumnFlags_WidthFixed);\n            ImGui::TableSetupColumn(\"BBB\", ImGuiTableColumnFlags_WidthFixed);\n            ImGui::TableSetupColumn(\"CCC\", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultHide);\n            ImGui::TableSetupColumn(\"DDD\", ImGuiTableColumnFlags_WidthStretch);\n            ImGui::TableSetupColumn(\"EEE\", ImGuiTableColumnFlags_WidthStretch);\n            ImGui::TableSetupColumn(\"FFF\", ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_DefaultHide);\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 6; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"%s %d,%d\", (column >= 3) ? \"Stretch\" : \"Fixed\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Reorderable, hideable, with headers\");\n    if (ImGui::TreeNode(\"Reorderable, hideable, with headers\"))\n    {\n        HelpMarker(\n            \"Click and drag column headers to reorder columns.\\n\\n\"\n            \"Right-click on a header to open a context menu.\");\n        static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Reorderable\", &flags, ImGuiTableFlags_Reorderable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Hideable\", &flags, ImGuiTableFlags_Hideable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBody\", &flags, ImGuiTableFlags_NoBordersInBody);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBodyUntilResize\", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker(\"Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_HighlightHoveredColumn\", &flags, ImGuiTableFlags_HighlightHoveredColumn);\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table1\", 3, flags))\n        {\n            // Submit columns name with TableSetupColumn() and call TableHeadersRow() to create a row with a header in each column.\n            // (Later we will show how TableSetupColumn() has other uses, optional flags, sizing weight etc.)\n            ImGui::TableSetupColumn(\"One\");\n            ImGui::TableSetupColumn(\"Two\");\n            ImGui::TableSetupColumn(\"Three\");\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 6; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Hello %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        // Use outer_size.x == 0.0f instead of default to make the table as tight as possible (only valid when no scrolling and no stretch column)\n        if (ImGui::BeginTable(\"table2\", 3, flags | ImGuiTableFlags_SizingFixedFit, ImVec2(0.0f, 0.0f)))\n        {\n            ImGui::TableSetupColumn(\"One\");\n            ImGui::TableSetupColumn(\"Two\");\n            ImGui::TableSetupColumn(\"Three\");\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 6; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Fixed %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Padding\");\n    if (ImGui::TreeNode(\"Padding\"))\n    {\n        // First example: showcase use of padding flags and effect of BorderOuterV/BorderInnerV on X padding.\n        // We don't expose BorderOuterH/BorderInnerH here because they have no effect on X padding.\n        HelpMarker(\n            \"We often want outer padding activated when any using features which makes the edges of a column visible:\\n\"\n            \"e.g.:\\n\"\n            \"- BorderOuterV\\n\"\n            \"- any form of row selection\\n\"\n            \"Because of this, activating BorderOuterV sets the default to PadOuterX. Using PadOuterX or NoPadOuterX you can override the default.\\n\\n\"\n            \"Actual padding values are using style.CellPadding.\\n\\n\"\n            \"In this demo we don't show horizontal borders to emphasize how they don't affect default horizontal padding.\");\n\n        static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_PadOuterX\", &flags1, ImGuiTableFlags_PadOuterX);\n        ImGui::SameLine(); HelpMarker(\"Enable outer-most padding (default if ImGuiTableFlags_BordersOuterV is set)\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoPadOuterX\", &flags1, ImGuiTableFlags_NoPadOuterX);\n        ImGui::SameLine(); HelpMarker(\"Disable outer-most padding (default if ImGuiTableFlags_BordersOuterV is not set)\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoPadInnerX\", &flags1, ImGuiTableFlags_NoPadInnerX);\n        ImGui::SameLine(); HelpMarker(\"Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off)\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterV\", &flags1, ImGuiTableFlags_BordersOuterV);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerV\", &flags1, ImGuiTableFlags_BordersInnerV);\n        static bool show_headers = false;\n        ImGui::Checkbox(\"show_headers\", &show_headers);\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table_padding\", 3, flags1))\n        {\n            if (show_headers)\n            {\n                ImGui::TableSetupColumn(\"One\");\n                ImGui::TableSetupColumn(\"Two\");\n                ImGui::TableSetupColumn(\"Three\");\n                ImGui::TableHeadersRow();\n            }\n\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    if (row == 0)\n                    {\n                        ImGui::Text(\"Avail %.2f\", ImGui::GetContentRegionAvail().x);\n                    }\n                    else\n                    {\n                        char buf[32];\n                        sprintf(buf, \"Hello %d,%d\", column, row);\n                        ImGui::Button(buf, ImVec2(-FLT_MIN, 0.0f));\n                    }\n                    //if (ImGui::TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered)\n                    //    ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, IM_COL32(0, 100, 0, 255));\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        // Second example: set style.CellPadding to (0.0) or a custom value.\n        // FIXME-TABLE: Vertical border effectively not displayed the same way as horizontal one...\n        HelpMarker(\"Setting style.CellPadding to (0,0) or a custom value.\");\n        static ImGuiTableFlags flags2 = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg;\n        static ImVec2 cell_padding(0.0f, 0.0f);\n        static bool show_widget_frame_bg = true;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Borders\", &flags2, ImGuiTableFlags_Borders);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersH\", &flags2, ImGuiTableFlags_BordersH);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersV\", &flags2, ImGuiTableFlags_BordersV);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInner\", &flags2, ImGuiTableFlags_BordersInner);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuter\", &flags2, ImGuiTableFlags_BordersOuter);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_RowBg\", &flags2, ImGuiTableFlags_RowBg);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags2, ImGuiTableFlags_Resizable);\n        ImGui::Checkbox(\"show_widget_frame_bg\", &show_widget_frame_bg);\n        ImGui::SliderFloat2(\"CellPadding\", &cell_padding.x, 0.0f, 10.0f, \"%.0f\");\n        PopStyleCompact();\n\n        ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cell_padding);\n        if (ImGui::BeginTable(\"table_padding_2\", 3, flags2))\n        {\n            static char text_bufs[3 * 5][16]; // Mini text storage for 3x5 cells\n            static bool init = true;\n            if (!show_widget_frame_bg)\n                ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);\n            for (int cell = 0; cell < 3 * 5; cell++)\n            {\n                ImGui::TableNextColumn();\n                if (init)\n                    strcpy(text_bufs[cell], \"edit me\");\n                ImGui::SetNextItemWidth(-FLT_MIN);\n                ImGui::PushID(cell);\n                ImGui::InputText(\"##cell\", text_bufs[cell], IM_ARRAYSIZE(text_bufs[cell]));\n                ImGui::PopID();\n            }\n            if (!show_widget_frame_bg)\n                ImGui::PopStyleColor();\n            init = false;\n            ImGui::EndTable();\n        }\n        ImGui::PopStyleVar();\n\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Explicit widths\");\n    if (ImGui::TreeNode(\"Sizing policies\"))\n    {\n        static ImGuiTableFlags flags1 = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags1, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendX\", &flags1, ImGuiTableFlags_NoHostExtendX);\n        PopStyleCompact();\n\n        static ImGuiTableFlags sizing_policy_flags[4] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame };\n        for (int table_n = 0; table_n < 4; table_n++)\n        {\n            ImGui::PushID(table_n);\n            ImGui::SetNextItemWidth(TEXT_BASE_WIDTH * 30);\n            EditTableSizingFlags(&sizing_policy_flags[table_n]);\n\n            // To make it easier to understand the different sizing policy,\n            // For each policy: we display one table where the columns have equal contents width, and one where the columns have different contents width.\n            if (ImGui::BeginTable(\"table1\", 3, sizing_policy_flags[table_n] | flags1))\n            {\n                for (int row = 0; row < 3; row++)\n                {\n                    ImGui::TableNextRow();\n                    ImGui::TableNextColumn(); ImGui::Text(\"Oh dear\");\n                    ImGui::TableNextColumn(); ImGui::Text(\"Oh dear\");\n                    ImGui::TableNextColumn(); ImGui::Text(\"Oh dear\");\n                }\n                ImGui::EndTable();\n            }\n            if (ImGui::BeginTable(\"table2\", 3, sizing_policy_flags[table_n] | flags1))\n            {\n                for (int row = 0; row < 3; row++)\n                {\n                    ImGui::TableNextRow();\n                    ImGui::TableNextColumn(); ImGui::Text(\"AAAA\");\n                    ImGui::TableNextColumn(); ImGui::Text(\"BBBBBBBB\");\n                    ImGui::TableNextColumn(); ImGui::Text(\"CCCCCCCCCCCC\");\n                }\n                ImGui::EndTable();\n            }\n            ImGui::PopID();\n        }\n\n        ImGui::Spacing();\n        ImGui::TextUnformatted(\"Advanced\");\n        ImGui::SameLine();\n        HelpMarker(\"This section allows you to interact and see the effect of various sizing policies depending on whether Scroll is enabled and the contents of your columns.\");\n\n        enum ContentsType { CT_ShowWidth, CT_ShortText, CT_LongText, CT_Button, CT_FillButton, CT_InputText };\n        static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable;\n        static int contents_type = CT_ShowWidth;\n        static int column_count = 3;\n\n        PushStyleCompact();\n        ImGui::PushID(\"Advanced\");\n        ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);\n        EditTableSizingFlags(&flags);\n        ImGui::Combo(\"Contents\", &contents_type, \"Show width\\0Short Text\\0Long Text\\0Button\\0Fill Button\\0InputText\\0\");\n        if (contents_type == CT_FillButton)\n        {\n            ImGui::SameLine();\n            HelpMarker(\"Be mindful that using right-alignment (e.g. size.x = -FLT_MIN) creates a feedback loop where contents width can feed into auto-column width can feed into contents width.\");\n        }\n        ImGui::DragInt(\"Columns\", &column_count, 0.1f, 1, 64, \"%d\", ImGuiSliderFlags_AlwaysClamp);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_PreciseWidths\", &flags, ImGuiTableFlags_PreciseWidths);\n        ImGui::SameLine(); HelpMarker(\"Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollX\", &flags, ImGuiTableFlags_ScrollX);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollY\", &flags, ImGuiTableFlags_ScrollY);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoClip\", &flags, ImGuiTableFlags_NoClip);\n        ImGui::PopItemWidth();\n        ImGui::PopID();\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table2\", column_count, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 7)))\n        {\n            for (int cell = 0; cell < 10 * column_count; cell++)\n            {\n                ImGui::TableNextColumn();\n                int column = ImGui::TableGetColumnIndex();\n                int row = ImGui::TableGetRowIndex();\n\n                ImGui::PushID(cell);\n                char label[32];\n                static char text_buf[32] = \"\";\n                sprintf(label, \"Hello %d,%d\", column, row);\n                switch (contents_type)\n                {\n                case CT_ShortText:  ImGui::TextUnformatted(label); break;\n                case CT_LongText:   ImGui::Text(\"Some %s text %d,%d\\nOver two lines..\", column == 0 ? \"long\" : \"longeeer\", column, row); break;\n                case CT_ShowWidth:  ImGui::Text(\"W: %.1f\", ImGui::GetContentRegionAvail().x); break;\n                case CT_Button:     ImGui::Button(label); break;\n                case CT_FillButton: ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f)); break;\n                case CT_InputText:  ImGui::SetNextItemWidth(-FLT_MIN); ImGui::InputText(\"##\", text_buf, IM_ARRAYSIZE(text_buf)); break;\n                }\n                ImGui::PopID();\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Vertical scrolling, with clipping\");\n    if (ImGui::TreeNode(\"Vertical scrolling, with clipping\"))\n    {\n        HelpMarker(\"Here we activate ScrollY, which will create a child window container to allow hosting scrollable contents.\\n\\nWe also demonstrate using ImGuiListClipper to virtualize the submission of many items.\");\n        static ImGuiTableFlags flags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollY\", &flags, ImGuiTableFlags_ScrollY);\n        PopStyleCompact();\n\n        // When using ScrollX or ScrollY we need to specify a size for our table container!\n        // Otherwise by default the table will fit all available space, like a BeginChild() call.\n        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);\n        if (ImGui::BeginTable(\"table_scrolly\", 3, flags, outer_size))\n        {\n            ImGui::TableSetupScrollFreeze(0, 1); // Make top row always visible\n            ImGui::TableSetupColumn(\"One\", ImGuiTableColumnFlags_None);\n            ImGui::TableSetupColumn(\"Two\", ImGuiTableColumnFlags_None);\n            ImGui::TableSetupColumn(\"Three\", ImGuiTableColumnFlags_None);\n            ImGui::TableHeadersRow();\n\n            // Demonstrate using clipper for large vertical lists\n            ImGuiListClipper clipper;\n            clipper.Begin(1000);\n            while (clipper.Step())\n            {\n                for (int row = clipper.DisplayStart; row < clipper.DisplayEnd; row++)\n                {\n                    ImGui::TableNextRow();\n                    for (int column = 0; column < 3; column++)\n                    {\n                        ImGui::TableSetColumnIndex(column);\n                        ImGui::Text(\"Hello %d,%d\", column, row);\n                    }\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Horizontal scrolling\");\n    if (ImGui::TreeNode(\"Horizontal scrolling\"))\n    {\n        HelpMarker(\n            \"When ScrollX is enabled, the default sizing policy becomes ImGuiTableFlags_SizingFixedFit, \"\n            \"as automatically stretching columns doesn't make much sense with horizontal scrolling.\\n\\n\"\n            \"Also note that as of the current version, you will almost always want to enable ScrollY along with ScrollX,\"\n            \"because the container window won't automatically extend vertically to fix contents (this may be improved in future versions).\");\n        static ImGuiTableFlags flags = ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable;\n        static int freeze_cols = 1;\n        static int freeze_rows = 1;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollX\", &flags, ImGuiTableFlags_ScrollX);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollY\", &flags, ImGuiTableFlags_ScrollY);\n        ImGui::SetNextItemWidth(ImGui::GetFrameHeight());\n        ImGui::DragInt(\"freeze_cols\", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);\n        ImGui::SetNextItemWidth(ImGui::GetFrameHeight());\n        ImGui::DragInt(\"freeze_rows\", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);\n        PopStyleCompact();\n\n        // When using ScrollX or ScrollY we need to specify a size for our table container!\n        // Otherwise by default the table will fit all available space, like a BeginChild() call.\n        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 8);\n        if (ImGui::BeginTable(\"table_scrollx\", 7, flags, outer_size))\n        {\n            ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);\n            ImGui::TableSetupColumn(\"Line #\", ImGuiTableColumnFlags_NoHide); // Make the first column not hideable to match our use of TableSetupScrollFreeze()\n            ImGui::TableSetupColumn(\"One\");\n            ImGui::TableSetupColumn(\"Two\");\n            ImGui::TableSetupColumn(\"Three\");\n            ImGui::TableSetupColumn(\"Four\");\n            ImGui::TableSetupColumn(\"Five\");\n            ImGui::TableSetupColumn(\"Six\");\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 20; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 7; column++)\n                {\n                    // Both TableNextColumn() and TableSetColumnIndex() return true when a column is visible or performing width measurement.\n                    // Because here we know that:\n                    // - A) all our columns are contributing the same to row height\n                    // - B) column 0 is always visible,\n                    // We only always submit this one column and can skip others.\n                    // More advanced per-column clipping behaviors may benefit from polling the status flags via TableGetColumnFlags().\n                    if (!ImGui::TableSetColumnIndex(column) && column > 0)\n                        continue;\n                    if (column == 0)\n                        ImGui::Text(\"Line %d\", row);\n                    else\n                        ImGui::Text(\"Hello world %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        ImGui::Spacing();\n        ImGui::TextUnformatted(\"Stretch + ScrollX\");\n        ImGui::SameLine();\n        HelpMarker(\n            \"Showcase using Stretch columns + ScrollX together: \"\n            \"this is rather unusual and only makes sense when specifying an 'inner_width' for the table!\\n\"\n            \"Without an explicit value, inner_width is == outer_size.x and therefore using Stretch columns + ScrollX together doesn't make sense.\");\n        static ImGuiTableFlags flags2 = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_RowBg | ImGuiTableFlags_ContextMenuInBody;\n        static float inner_width = 1000.0f;\n        PushStyleCompact();\n        ImGui::PushID(\"flags3\");\n        ImGui::PushItemWidth(TEXT_BASE_WIDTH * 30);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollX\", &flags2, ImGuiTableFlags_ScrollX);\n        ImGui::DragFloat(\"inner_width\", &inner_width, 1.0f, 0.0f, FLT_MAX, \"%.1f\");\n        ImGui::PopItemWidth();\n        ImGui::PopID();\n        PopStyleCompact();\n        if (ImGui::BeginTable(\"table2\", 7, flags2, outer_size, inner_width))\n        {\n            for (int cell = 0; cell < 20 * 7; cell++)\n            {\n                ImGui::TableNextColumn();\n                ImGui::Text(\"Hello world %d,%d\", ImGui::TableGetColumnIndex(), ImGui::TableGetRowIndex());\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Columns flags\");\n    if (ImGui::TreeNode(\"Columns flags\"))\n    {\n        // Create a first table just to show all the options/flags we want to make visible in our example!\n        const int column_count = 3;\n        const char* column_names[column_count] = { \"One\", \"Two\", \"Three\" };\n        static ImGuiTableColumnFlags column_flags[column_count] = { ImGuiTableColumnFlags_DefaultSort, ImGuiTableColumnFlags_None, ImGuiTableColumnFlags_DefaultHide };\n        static ImGuiTableColumnFlags column_flags_out[column_count] = { 0, 0, 0 }; // Output from TableGetColumnFlags()\n\n        if (ImGui::BeginTable(\"table_columns_flags_checkboxes\", column_count, ImGuiTableFlags_None))\n        {\n            PushStyleCompact();\n            for (int column = 0; column < column_count; column++)\n            {\n                ImGui::TableNextColumn();\n                ImGui::PushID(column);\n                ImGui::AlignTextToFramePadding(); // FIXME-TABLE: Workaround for wrong text baseline propagation across columns\n                ImGui::Text(\"'%s'\", column_names[column]);\n                ImGui::Spacing();\n                ImGui::Text(\"Input flags:\");\n                EditTableColumnsFlags(&column_flags[column]);\n                ImGui::Spacing();\n                ImGui::Text(\"Output flags:\");\n                ImGui::BeginDisabled();\n                ShowTableColumnsStatusFlags(column_flags_out[column]);\n                ImGui::EndDisabled();\n                ImGui::PopID();\n            }\n            PopStyleCompact();\n            ImGui::EndTable();\n        }\n\n        // Create the real table we care about for the example!\n        // We use a scrolling table to be able to showcase the difference between the _IsEnabled and _IsVisible flags above, otherwise in\n        // a non-scrolling table columns are always visible (unless using ImGuiTableFlags_NoKeepColumnsVisible + resizing the parent window down)\n        const ImGuiTableFlags flags\n            = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY\n            | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV\n            | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable;\n        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 9);\n        if (ImGui::BeginTable(\"table_columns_flags\", column_count, flags, outer_size))\n        {\n            bool has_angled_header = false;\n            for (int column = 0; column < column_count; column++)\n            {\n                has_angled_header |= (column_flags[column] & ImGuiTableColumnFlags_AngledHeader) != 0;\n                ImGui::TableSetupColumn(column_names[column], column_flags[column]);\n            }\n            if (has_angled_header)\n                ImGui::TableAngledHeadersRow();\n            ImGui::TableHeadersRow();\n            for (int column = 0; column < column_count; column++)\n                column_flags_out[column] = ImGui::TableGetColumnFlags(column);\n            float indent_step = (float)((int)TEXT_BASE_WIDTH / 2);\n            for (int row = 0; row < 8; row++)\n            {\n                ImGui::Indent(indent_step); // Add some indentation to demonstrate usage of per-column IndentEnable/IndentDisable flags.\n                ImGui::TableNextRow();\n                for (int column = 0; column < column_count; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"%s %s\", (column == 0) ? \"Indented\" : \"Hello\", ImGui::TableGetColumnName(column));\n                }\n            }\n            ImGui::Unindent(indent_step * 8.0f);\n\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Columns widths\");\n    if (ImGui::TreeNode(\"Columns widths\"))\n    {\n        HelpMarker(\"Using TableSetupColumn() to setup default width.\");\n\n        static ImGuiTableFlags flags1 = ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBodyUntilResize;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags1, ImGuiTableFlags_Resizable);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBodyUntilResize\", &flags1, ImGuiTableFlags_NoBordersInBodyUntilResize);\n        PopStyleCompact();\n        if (ImGui::BeginTable(\"table1\", 3, flags1))\n        {\n            // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed.\n            ImGui::TableSetupColumn(\"one\", ImGuiTableColumnFlags_WidthFixed, 100.0f); // Default to 100.0f\n            ImGui::TableSetupColumn(\"two\", ImGuiTableColumnFlags_WidthFixed, 200.0f); // Default to 200.0f\n            ImGui::TableSetupColumn(\"three\", ImGuiTableColumnFlags_WidthFixed);       // Default to auto\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 4; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    if (row == 0)\n                        ImGui::Text(\"(w: %5.1f)\", ImGui::GetContentRegionAvail().x);\n                    else\n                        ImGui::Text(\"Hello %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        HelpMarker(\"Using TableSetupColumn() to setup explicit width.\\n\\nUnless _NoKeepColumnsVisible is set, fixed columns with set width may still be shrunk down if there's not enough space in the host.\");\n\n        static ImGuiTableFlags flags2 = ImGuiTableFlags_None;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoKeepColumnsVisible\", &flags2, ImGuiTableFlags_NoKeepColumnsVisible);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerV\", &flags2, ImGuiTableFlags_BordersInnerV);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterV\", &flags2, ImGuiTableFlags_BordersOuterV);\n        PopStyleCompact();\n        if (ImGui::BeginTable(\"table2\", 4, flags2))\n        {\n            // We could also set ImGuiTableFlags_SizingFixedFit on the table and all columns will default to ImGuiTableColumnFlags_WidthFixed.\n            ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthFixed, 100.0f);\n            ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);\n            ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 30.0f);\n            ImGui::TableSetupColumn(\"\", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 15.0f);\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 4; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    if (row == 0)\n                        ImGui::Text(\"(w: %5.1f)\", ImGui::GetContentRegionAvail().x);\n                    else\n                        ImGui::Text(\"Hello %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Nested tables\");\n    if (ImGui::TreeNode(\"Nested tables\"))\n    {\n        HelpMarker(\"This demonstrates embedding a table into another table cell.\");\n\n        if (ImGui::BeginTable(\"table_nested1\", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))\n        {\n            ImGui::TableSetupColumn(\"A0\");\n            ImGui::TableSetupColumn(\"A1\");\n            ImGui::TableHeadersRow();\n\n            ImGui::TableNextColumn();\n            ImGui::Text(\"A0 Row 0\");\n            {\n                float rows_height = TEXT_BASE_HEIGHT * 2;\n                if (ImGui::BeginTable(\"table_nested2\", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))\n                {\n                    ImGui::TableSetupColumn(\"B0\");\n                    ImGui::TableSetupColumn(\"B1\");\n                    ImGui::TableHeadersRow();\n\n                    ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"B0 Row 0\");\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"B1 Row 0\");\n                    ImGui::TableNextRow(ImGuiTableRowFlags_None, rows_height);\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"B0 Row 1\");\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"B1 Row 1\");\n\n                    ImGui::EndTable();\n                }\n            }\n            ImGui::TableNextColumn(); ImGui::Text(\"A1 Row 0\");\n            ImGui::TableNextColumn(); ImGui::Text(\"A0 Row 1\");\n            ImGui::TableNextColumn(); ImGui::Text(\"A1 Row 1\");\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Row height\");\n    if (ImGui::TreeNode(\"Row height\"))\n    {\n        HelpMarker(\"You can pass a 'min_row_height' to TableNextRow().\\n\\nRows are padded with 'style.CellPadding.y' on top and bottom, so effectively the minimum row height will always be >= 'style.CellPadding.y * 2.0f'.\\n\\nWe cannot honor a _maximum_ row height as that would require a unique clipping rectangle per row.\");\n        if (ImGui::BeginTable(\"table_row_height\", 1, ImGuiTableFlags_Borders))\n        {\n            for (int row = 0; row < 8; row++)\n            {\n                float min_row_height = (float)(int)(TEXT_BASE_HEIGHT * 0.30f * row);\n                ImGui::TableNextRow(ImGuiTableRowFlags_None, min_row_height);\n                ImGui::TableNextColumn();\n                ImGui::Text(\"min_row_height = %.2f\", min_row_height);\n            }\n            ImGui::EndTable();\n        }\n\n        HelpMarker(\"Showcase using SameLine(0,0) to share Current Line Height between cells.\\n\\nPlease note that Tables Row Height is not the same thing as Current Line Height, as a table cell may contains multiple lines.\");\n        if (ImGui::BeginTable(\"table_share_lineheight\", 2, ImGuiTableFlags_Borders))\n        {\n            ImGui::TableNextRow();\n            ImGui::TableNextColumn();\n            ImGui::ColorButton(\"##1\", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40));\n            ImGui::TableNextColumn();\n            ImGui::Text(\"Line 1\");\n            ImGui::Text(\"Line 2\");\n\n            ImGui::TableNextRow();\n            ImGui::TableNextColumn();\n            ImGui::ColorButton(\"##2\", ImVec4(0.13f, 0.26f, 0.40f, 1.0f), ImGuiColorEditFlags_None, ImVec2(40, 40));\n            ImGui::TableNextColumn();\n            ImGui::SameLine(0.0f, 0.0f); // Reuse line height from previous column\n            ImGui::Text(\"Line 1, with SameLine(0,0)\");\n            ImGui::Text(\"Line 2\");\n\n            ImGui::EndTable();\n        }\n\n        HelpMarker(\"Showcase altering CellPadding.y between rows. Note that CellPadding.x is locked for the entire table.\");\n        if (ImGui::BeginTable(\"table_changing_cellpadding_y\", 1, ImGuiTableFlags_Borders))\n        {\n            ImGuiStyle& style = ImGui::GetStyle();\n            for (int row = 0; row < 8; row++)\n            {\n                if ((row % 3) == 2)\n                    ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(style.CellPadding.x, 20.0f));\n                ImGui::TableNextRow(ImGuiTableRowFlags_None);\n                ImGui::TableNextColumn();\n                ImGui::Text(\"CellPadding.y = %.2f\", style.CellPadding.y);\n                if ((row % 3) == 2)\n                    ImGui::PopStyleVar();\n            }\n            ImGui::EndTable();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Outer size\");\n    if (ImGui::TreeNode(\"Outer size\"))\n    {\n        // Showcasing use of ImGuiTableFlags_NoHostExtendX and ImGuiTableFlags_NoHostExtendY\n        // Important to that note how the two flags have slightly different behaviors!\n        ImGui::Text(\"Using NoHostExtendX and NoHostExtendY:\");\n        PushStyleCompact();\n        static ImGuiTableFlags flags = ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendX\", &flags, ImGuiTableFlags_NoHostExtendX);\n        ImGui::SameLine(); HelpMarker(\"Make outer width auto-fit to columns, overriding outer_size.x value.\\n\\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used.\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendY\", &flags, ImGuiTableFlags_NoHostExtendY);\n        ImGui::SameLine(); HelpMarker(\"Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\\n\\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.\");\n        PopStyleCompact();\n\n        ImVec2 outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);\n        if (ImGui::BeginTable(\"table1\", 3, flags, outer_size))\n        {\n            for (int row = 0; row < 10; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"Cell %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::SameLine();\n        ImGui::Text(\"Hello!\");\n\n        ImGui::Spacing();\n\n        ImGui::Text(\"Using explicit size:\");\n        if (ImGui::BeginTable(\"table2\", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))\n        {\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"Cell %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::SameLine();\n        if (ImGui::BeginTable(\"table3\", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg, ImVec2(TEXT_BASE_WIDTH * 30, 0.0f)))\n        {\n            for (int row = 0; row < 3; row++)\n            {\n                ImGui::TableNextRow(0, TEXT_BASE_HEIGHT * 1.5f);\n                for (int column = 0; column < 3; column++)\n                {\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"Cell %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Background color\");\n    if (ImGui::TreeNode(\"Background color\"))\n    {\n        static ImGuiTableFlags flags = ImGuiTableFlags_RowBg;\n        static int row_bg_type = 1;\n        static int row_bg_target = 1;\n        static int cell_bg_type = 1;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_Borders\", &flags, ImGuiTableFlags_Borders);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_RowBg\", &flags, ImGuiTableFlags_RowBg);\n        ImGui::SameLine(); HelpMarker(\"ImGuiTableFlags_RowBg automatically sets RowBg0 to alternative colors pulled from the Style.\");\n        ImGui::Combo(\"row bg type\", (int*)&row_bg_type, \"None\\0Red\\0Gradient\\0\");\n        ImGui::Combo(\"row bg target\", (int*)&row_bg_target, \"RowBg0\\0RowBg1\\0\"); ImGui::SameLine(); HelpMarker(\"Target RowBg0 to override the alternating odd/even colors,\\nTarget RowBg1 to blend with them.\");\n        ImGui::Combo(\"cell bg type\", (int*)&cell_bg_type, \"None\\0Blue\\0\"); ImGui::SameLine(); HelpMarker(\"We are colorizing cells to B1->C2 here.\");\n        IM_ASSERT(row_bg_type >= 0 && row_bg_type <= 2);\n        IM_ASSERT(row_bg_target >= 0 && row_bg_target <= 1);\n        IM_ASSERT(cell_bg_type >= 0 && cell_bg_type <= 1);\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table1\", 5, flags))\n        {\n            for (int row = 0; row < 6; row++)\n            {\n                ImGui::TableNextRow();\n\n                // Demonstrate setting a row background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBgX, ...)'\n                // We use a transparent color so we can see the one behind in case our target is RowBg1 and RowBg0 was already targeted by the ImGuiTableFlags_RowBg flag.\n                if (row_bg_type != 0)\n                {\n                    ImU32 row_bg_color = ImGui::GetColorU32(row_bg_type == 1 ? ImVec4(0.7f, 0.3f, 0.3f, 0.65f) : ImVec4(0.2f + row * 0.1f, 0.2f, 0.2f, 0.65f)); // Flat or Gradient?\n                    ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0 + row_bg_target, row_bg_color);\n                }\n\n                // Fill cells\n                for (int column = 0; column < 5; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"%c%c\", 'A' + row, '0' + column);\n\n                    // Change background of Cells B1->C2\n                    // Demonstrate setting a cell background color with 'ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ...)'\n                    // (the CellBg color will be blended over the RowBg and ColumnBg colors)\n                    // We can also pass a column number as a third parameter to TableSetBgColor() and do this outside the column loop.\n                    if (row >= 1 && row <= 2 && column >= 1 && column <= 2 && cell_bg_type == 1)\n                    {\n                        ImU32 cell_bg_color = ImGui::GetColorU32(ImVec4(0.3f, 0.3f, 0.7f, 0.65f));\n                        ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color);\n                    }\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Tree view\");\n    if (ImGui::TreeNode(\"Tree view\"))\n    {\n        static ImGuiTableFlags flags = ImGuiTableFlags_BordersV | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody;\n\n        static ImGuiTreeNodeFlags tree_node_flags = ImGuiTreeNodeFlags_SpanAllColumns;\n        ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanFullWidth\", &tree_node_flags, ImGuiTreeNodeFlags_SpanFullWidth);\n        ImGui::CheckboxFlags(\"ImGuiTreeNodeFlags_SpanAllColumns\", &tree_node_flags, ImGuiTreeNodeFlags_SpanAllColumns);\n\n        HelpMarker(\"See \\\"Columns flags\\\" section to configure how indentation is applied to individual columns.\");\n        if (ImGui::BeginTable(\"3ways\", 3, flags))\n        {\n            // The first column will use the default _WidthStretch when ScrollX is Off and _WidthFixed when ScrollX is On\n            ImGui::TableSetupColumn(\"Name\", ImGuiTableColumnFlags_NoHide);\n            ImGui::TableSetupColumn(\"Size\", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f);\n            ImGui::TableSetupColumn(\"Type\", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f);\n            ImGui::TableHeadersRow();\n\n            // Simple storage to output a dummy file-system.\n            struct MyTreeNode\n            {\n                const char*     Name;\n                const char*     Type;\n                int             Size;\n                int             ChildIdx;\n                int             ChildCount;\n                static void DisplayNode(const MyTreeNode* node, const MyTreeNode* all_nodes)\n                {\n                    ImGui::TableNextRow();\n                    ImGui::TableNextColumn();\n                    const bool is_folder = (node->ChildCount > 0);\n                    if (is_folder)\n                    {\n                        bool open = ImGui::TreeNodeEx(node->Name, tree_node_flags);\n                        ImGui::TableNextColumn();\n                        ImGui::TextDisabled(\"--\");\n                        ImGui::TableNextColumn();\n                        ImGui::TextUnformatted(node->Type);\n                        if (open)\n                        {\n                            for (int child_n = 0; child_n < node->ChildCount; child_n++)\n                                DisplayNode(&all_nodes[node->ChildIdx + child_n], all_nodes);\n                            ImGui::TreePop();\n                        }\n                    }\n                    else\n                    {\n                        ImGui::TreeNodeEx(node->Name, tree_node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet | ImGuiTreeNodeFlags_NoTreePushOnOpen);\n                        ImGui::TableNextColumn();\n                        ImGui::Text(\"%d\", node->Size);\n                        ImGui::TableNextColumn();\n                        ImGui::TextUnformatted(node->Type);\n                    }\n                }\n            };\n            static const MyTreeNode nodes[] =\n            {\n                { \"Root\",                         \"Folder\",       -1,       1, 3    }, // 0\n                { \"Music\",                        \"Folder\",       -1,       4, 2    }, // 1\n                { \"Textures\",                     \"Folder\",       -1,       6, 3    }, // 2\n                { \"desktop.ini\",                  \"System file\",  1024,    -1,-1    }, // 3\n                { \"File1_a.wav\",                  \"Audio file\",   123000,  -1,-1    }, // 4\n                { \"File1_b.wav\",                  \"Audio file\",   456000,  -1,-1    }, // 5\n                { \"Image001.png\",                 \"Image file\",   203128,  -1,-1    }, // 6\n                { \"Copy of Image001.png\",         \"Image file\",   203256,  -1,-1    }, // 7\n                { \"Copy of Image001 (Final2).png\",\"Image file\",   203512,  -1,-1    }, // 8\n            };\n\n            MyTreeNode::DisplayNode(&nodes[0], nodes);\n\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Item width\");\n    if (ImGui::TreeNode(\"Item width\"))\n    {\n        HelpMarker(\n            \"Showcase using PushItemWidth() and how it is preserved on a per-column basis.\\n\\n\"\n            \"Note that on auto-resizing non-resizable fixed columns, querying the content width for e.g. right-alignment doesn't make sense.\");\n        if (ImGui::BeginTable(\"table_item_width\", 3, ImGuiTableFlags_Borders))\n        {\n            ImGui::TableSetupColumn(\"small\");\n            ImGui::TableSetupColumn(\"half\");\n            ImGui::TableSetupColumn(\"right-align\");\n            ImGui::TableHeadersRow();\n\n            for (int row = 0; row < 3; row++)\n            {\n                ImGui::TableNextRow();\n                if (row == 0)\n                {\n                    // Setup ItemWidth once (instead of setting up every time, which is also possible but less efficient)\n                    ImGui::TableSetColumnIndex(0);\n                    ImGui::PushItemWidth(TEXT_BASE_WIDTH * 3.0f); // Small\n                    ImGui::TableSetColumnIndex(1);\n                    ImGui::PushItemWidth(-ImGui::GetContentRegionAvail().x * 0.5f);\n                    ImGui::TableSetColumnIndex(2);\n                    ImGui::PushItemWidth(-FLT_MIN); // Right-aligned\n                }\n\n                // Draw our contents\n                static float dummy_f = 0.0f;\n                ImGui::PushID(row);\n                ImGui::TableSetColumnIndex(0);\n                ImGui::SliderFloat(\"float0\", &dummy_f, 0.0f, 1.0f);\n                ImGui::TableSetColumnIndex(1);\n                ImGui::SliderFloat(\"float1\", &dummy_f, 0.0f, 1.0f);\n                ImGui::TableSetColumnIndex(2);\n                ImGui::SliderFloat(\"##float2\", &dummy_f, 0.0f, 1.0f); // No visible label since right-aligned\n                ImGui::PopID();\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    // Demonstrate using TableHeader() calls instead of TableHeadersRow()\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Custom headers\");\n    if (ImGui::TreeNode(\"Custom headers\"))\n    {\n        const int COLUMNS_COUNT = 3;\n        if (ImGui::BeginTable(\"table_custom_headers\", COLUMNS_COUNT, ImGuiTableFlags_Borders | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))\n        {\n            ImGui::TableSetupColumn(\"Apricot\");\n            ImGui::TableSetupColumn(\"Banana\");\n            ImGui::TableSetupColumn(\"Cherry\");\n\n            // Dummy entire-column selection storage\n            // FIXME: It would be nice to actually demonstrate full-featured selection using those checkbox.\n            static bool column_selected[3] = {};\n\n            // Instead of calling TableHeadersRow() we'll submit custom headers ourselves\n            ImGui::TableNextRow(ImGuiTableRowFlags_Headers);\n            for (int column = 0; column < COLUMNS_COUNT; column++)\n            {\n                ImGui::TableSetColumnIndex(column);\n                const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn()\n                ImGui::PushID(column);\n                ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));\n                ImGui::Checkbox(\"##checkall\", &column_selected[column]);\n                ImGui::PopStyleVar();\n                ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);\n                ImGui::TableHeader(column_name);\n                ImGui::PopID();\n            }\n\n            for (int row = 0; row < 5; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < 3; column++)\n                {\n                    char buf[32];\n                    sprintf(buf, \"Cell %d,%d\", column, row);\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Selectable(buf, column_selected[column]);\n                }\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    // Demonstrate using ImGuiTableColumnFlags_AngledHeader flag to create angled headers\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Angled headers\");\n    if (ImGui::TreeNode(\"Angled headers\"))\n    {\n        const char* column_names[] = { \"Track\", \"cabasa\", \"ride\", \"smash\", \"tom-hi\", \"tom-mid\", \"tom-low\", \"hihat-o\", \"hihat-c\", \"snare-s\", \"snare-c\", \"clap\", \"rim\", \"kick\" };\n        const int columns_count = IM_ARRAYSIZE(column_names);\n        const int rows_count = 12;\n\n        static ImGuiTableFlags table_flags = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_HighlightHoveredColumn;\n        static bool bools[columns_count * rows_count] = {}; // Dummy storage selection storage\n        static int frozen_cols = 1;\n        static int frozen_rows = 2;\n        ImGui::CheckboxFlags(\"_ScrollX\", &table_flags, ImGuiTableFlags_ScrollX);\n        ImGui::CheckboxFlags(\"_ScrollY\", &table_flags, ImGuiTableFlags_ScrollY);\n        ImGui::CheckboxFlags(\"_NoBordersInBody\", &table_flags, ImGuiTableFlags_NoBordersInBody);\n        ImGui::CheckboxFlags(\"_HighlightHoveredColumn\", &table_flags, ImGuiTableFlags_HighlightHoveredColumn);\n        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n        ImGui::SliderInt(\"Frozen columns\", &frozen_cols, 0, 2);\n        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n        ImGui::SliderInt(\"Frozen rows\", &frozen_rows, 0, 2);\n\n        if (ImGui::BeginTable(\"table_angled_headers\", columns_count, table_flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 12)))\n        {\n            ImGui::TableSetupColumn(column_names[0], ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder);\n            for (int n = 1; n < columns_count; n++)\n                ImGui::TableSetupColumn(column_names[n], ImGuiTableColumnFlags_AngledHeader | ImGuiTableColumnFlags_WidthFixed);\n            ImGui::TableSetupScrollFreeze(frozen_cols, frozen_rows);\n\n            ImGui::TableAngledHeadersRow(); // Draw angled headers for all columns with the ImGuiTableColumnFlags_AngledHeader flag.\n            ImGui::TableHeadersRow();       // Draw remaining headers and allow access to context-menu and other functions.\n            for (int row = 0; row < rows_count; row++)\n            {\n                ImGui::PushID(row);\n                ImGui::TableNextRow();\n                ImGui::TableSetColumnIndex(0);\n                ImGui::AlignTextToFramePadding();\n                ImGui::Text(\"Track %d\", row);\n                for (int column = 1; column < columns_count; column++)\n                    if (ImGui::TableSetColumnIndex(column))\n                    {\n                        ImGui::PushID(column);\n                        ImGui::Checkbox(\"\", &bools[row * columns_count + column]);\n                        ImGui::PopID();\n                    }\n                ImGui::PopID();\n            }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    // Demonstrate creating custom context menus inside columns, while playing it nice with context menus provided by TableHeadersRow()/TableHeader()\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Context menus\");\n    if (ImGui::TreeNode(\"Context menus\"))\n    {\n        HelpMarker(\"By default, right-clicking over a TableHeadersRow()/TableHeader() line will open the default context-menu.\\nUsing ImGuiTableFlags_ContextMenuInBody we also allow right-clicking over columns body.\");\n        static ImGuiTableFlags flags1 = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ContextMenuInBody;\n\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ContextMenuInBody\", &flags1, ImGuiTableFlags_ContextMenuInBody);\n        PopStyleCompact();\n\n        // Context Menus: first example\n        // [1.1] Right-click on the TableHeadersRow() line to open the default table context menu.\n        // [1.2] Right-click in columns also open the default table context menu (if ImGuiTableFlags_ContextMenuInBody is set)\n        const int COLUMNS_COUNT = 3;\n        if (ImGui::BeginTable(\"table_context_menu\", COLUMNS_COUNT, flags1))\n        {\n            ImGui::TableSetupColumn(\"One\");\n            ImGui::TableSetupColumn(\"Two\");\n            ImGui::TableSetupColumn(\"Three\");\n\n            // [1.1]] Right-click on the TableHeadersRow() line to open the default table context menu.\n            ImGui::TableHeadersRow();\n\n            // Submit dummy contents\n            for (int row = 0; row < 4; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < COLUMNS_COUNT; column++)\n                {\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Cell %d,%d\", column, row);\n                }\n            }\n            ImGui::EndTable();\n        }\n\n        // Context Menus: second example\n        // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.\n        // [2.2] Right-click on the \"..\" to open a custom popup\n        // [2.3] Right-click in columns to open another custom popup\n        HelpMarker(\"Demonstrate mixing table context menu (over header), item context button (over button) and custom per-colum context menu (over column body).\");\n        ImGuiTableFlags flags2 = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders;\n        if (ImGui::BeginTable(\"table_context_menu_2\", COLUMNS_COUNT, flags2))\n        {\n            ImGui::TableSetupColumn(\"One\");\n            ImGui::TableSetupColumn(\"Two\");\n            ImGui::TableSetupColumn(\"Three\");\n\n            // [2.1] Right-click on the TableHeadersRow() line to open the default table context menu.\n            ImGui::TableHeadersRow();\n            for (int row = 0; row < 4; row++)\n            {\n                ImGui::TableNextRow();\n                for (int column = 0; column < COLUMNS_COUNT; column++)\n                {\n                    // Submit dummy contents\n                    ImGui::TableSetColumnIndex(column);\n                    ImGui::Text(\"Cell %d,%d\", column, row);\n                    ImGui::SameLine();\n\n                    // [2.2] Right-click on the \"..\" to open a custom popup\n                    ImGui::PushID(row * COLUMNS_COUNT + column);\n                    ImGui::SmallButton(\"..\");\n                    if (ImGui::BeginPopupContextItem())\n                    {\n                        ImGui::Text(\"This is the popup for Button(\\\"..\\\") in Cell %d,%d\", column, row);\n                        if (ImGui::Button(\"Close\"))\n                            ImGui::CloseCurrentPopup();\n                        ImGui::EndPopup();\n                    }\n                    ImGui::PopID();\n                }\n            }\n\n            // [2.3] Right-click anywhere in columns to open another custom popup\n            // (instead of testing for !IsAnyItemHovered() we could also call OpenPopup() with ImGuiPopupFlags_NoOpenOverExistingPopup\n            // to manage popup priority as the popups triggers, here \"are we hovering a column\" are overlapping)\n            int hovered_column = -1;\n            for (int column = 0; column < COLUMNS_COUNT + 1; column++)\n            {\n                ImGui::PushID(column);\n                if (ImGui::TableGetColumnFlags(column) & ImGuiTableColumnFlags_IsHovered)\n                    hovered_column = column;\n                if (hovered_column == column && !ImGui::IsAnyItemHovered() && ImGui::IsMouseReleased(1))\n                    ImGui::OpenPopup(\"MyPopup\");\n                if (ImGui::BeginPopup(\"MyPopup\"))\n                {\n                    if (column == COLUMNS_COUNT)\n                        ImGui::Text(\"This is a custom popup for unused space after the last column.\");\n                    else\n                        ImGui::Text(\"This is a custom popup for Column %d\", column);\n                    if (ImGui::Button(\"Close\"))\n                        ImGui::CloseCurrentPopup();\n                    ImGui::EndPopup();\n                }\n                ImGui::PopID();\n            }\n\n            ImGui::EndTable();\n            ImGui::Text(\"Hovered column: %d\", hovered_column);\n        }\n        ImGui::TreePop();\n    }\n\n    // Demonstrate creating multiple tables with the same ID\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Synced instances\");\n    if (ImGui::TreeNode(\"Synced instances\"))\n    {\n        HelpMarker(\"Multiple tables with the same identifier will share their settings, width, visibility, order etc.\");\n\n        static ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoSavedSettings;\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollY\", &flags, ImGuiTableFlags_ScrollY);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_SizingFixedFit\", &flags, ImGuiTableFlags_SizingFixedFit);\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_HighlightHoveredColumn\", &flags, ImGuiTableFlags_HighlightHoveredColumn);\n        for (int n = 0; n < 3; n++)\n        {\n            char buf[32];\n            sprintf(buf, \"Synced Table %d\", n);\n            bool open = ImGui::CollapsingHeader(buf, ImGuiTreeNodeFlags_DefaultOpen);\n            if (open && ImGui::BeginTable(\"Table\", 3, flags, ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 5)))\n            {\n                ImGui::TableSetupColumn(\"One\");\n                ImGui::TableSetupColumn(\"Two\");\n                ImGui::TableSetupColumn(\"Three\");\n                ImGui::TableHeadersRow();\n                const int cell_count = (n == 1) ? 27 : 9; // Make second table have a scrollbar to verify that additional decoration is not affecting column positions.\n                for (int cell = 0; cell < cell_count; cell++)\n                {\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"this cell %d\", cell);\n                }\n                ImGui::EndTable();\n            }\n        }\n        ImGui::TreePop();\n    }\n\n    // Demonstrate using Sorting facilities\n    // This is a simplified version of the \"Advanced\" example, where we mostly focus on the code necessary to handle sorting.\n    // Note that the \"Advanced\" example also showcase manually triggering a sort (e.g. if item quantities have been modified)\n    static const char* template_items_names[] =\n    {\n        \"Banana\", \"Apple\", \"Cherry\", \"Watermelon\", \"Grapefruit\", \"Strawberry\", \"Mango\",\n        \"Kiwi\", \"Orange\", \"Pineapple\", \"Blueberry\", \"Plum\", \"Coconut\", \"Pear\", \"Apricot\"\n    };\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Sorting\");\n    if (ImGui::TreeNode(\"Sorting\"))\n    {\n        // Create item list\n        static ImVector<MyItem> items;\n        if (items.Size == 0)\n        {\n            items.resize(50, MyItem());\n            for (int n = 0; n < items.Size; n++)\n            {\n                const int template_n = n % IM_ARRAYSIZE(template_items_names);\n                MyItem& item = items[n];\n                item.ID = n;\n                item.Name = template_items_names[template_n];\n                item.Quantity = (n * n - n) % 20; // Assign default quantities\n            }\n        }\n\n        // Options\n        static ImGuiTableFlags flags =\n            ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti\n            | ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_NoBordersInBody\n            | ImGuiTableFlags_ScrollY;\n        PushStyleCompact();\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_SortMulti\", &flags, ImGuiTableFlags_SortMulti);\n        ImGui::SameLine(); HelpMarker(\"When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).\");\n        ImGui::CheckboxFlags(\"ImGuiTableFlags_SortTristate\", &flags, ImGuiTableFlags_SortTristate);\n        ImGui::SameLine(); HelpMarker(\"When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).\");\n        PopStyleCompact();\n\n        if (ImGui::BeginTable(\"table_sorting\", 4, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 15), 0.0f))\n        {\n            // Declare columns\n            // We use the \"user_id\" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.\n            // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!\n            // Demonstrate using a mixture of flags among available sort-related flags:\n            // - ImGuiTableColumnFlags_DefaultSort\n            // - ImGuiTableColumnFlags_NoSort / ImGuiTableColumnFlags_NoSortAscending / ImGuiTableColumnFlags_NoSortDescending\n            // - ImGuiTableColumnFlags_PreferSortAscending / ImGuiTableColumnFlags_PreferSortDescending\n            ImGui::TableSetupColumn(\"ID\",       ImGuiTableColumnFlags_DefaultSort          | ImGuiTableColumnFlags_WidthFixed,   0.0f, MyItemColumnID_ID);\n            ImGui::TableSetupColumn(\"Name\",                                                  ImGuiTableColumnFlags_WidthFixed,   0.0f, MyItemColumnID_Name);\n            ImGui::TableSetupColumn(\"Action\",   ImGuiTableColumnFlags_NoSort               | ImGuiTableColumnFlags_WidthFixed,   0.0f, MyItemColumnID_Action);\n            ImGui::TableSetupColumn(\"Quantity\", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthStretch, 0.0f, MyItemColumnID_Quantity);\n            ImGui::TableSetupScrollFreeze(0, 1); // Make row always visible\n            ImGui::TableHeadersRow();\n\n            // Sort our data if sort specs have been changed!\n            if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs())\n                if (sort_specs->SpecsDirty)\n                {\n                    MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size);\n                    sort_specs->SpecsDirty = false;\n                }\n\n            // Demonstrate using clipper for large vertical lists\n            ImGuiListClipper clipper;\n            clipper.Begin(items.Size);\n            while (clipper.Step())\n                for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)\n                {\n                    // Display a data item\n                    MyItem* item = &items[row_n];\n                    ImGui::PushID(item->ID);\n                    ImGui::TableNextRow();\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"%04d\", item->ID);\n                    ImGui::TableNextColumn();\n                    ImGui::TextUnformatted(item->Name);\n                    ImGui::TableNextColumn();\n                    ImGui::SmallButton(\"None\");\n                    ImGui::TableNextColumn();\n                    ImGui::Text(\"%d\", item->Quantity);\n                    ImGui::PopID();\n                }\n            ImGui::EndTable();\n        }\n        ImGui::TreePop();\n    }\n\n    // In this example we'll expose most table flags and settings.\n    // For specific flags and settings refer to the corresponding section for more detailed explanation.\n    // This section is mostly useful to experiment with combining certain flags or settings with each others.\n    //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // [DEBUG]\n    if (open_action != -1)\n        ImGui::SetNextItemOpen(open_action != 0);\n    IMGUI_DEMO_MARKER(\"Tables/Advanced\");\n    if (ImGui::TreeNode(\"Advanced\"))\n    {\n        static ImGuiTableFlags flags =\n            ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable\n            | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti\n            | ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_NoBordersInBody\n            | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY\n            | ImGuiTableFlags_SizingFixedFit;\n        static ImGuiTableColumnFlags columns_base_flags = ImGuiTableColumnFlags_None;\n\n        enum ContentsType { CT_Text, CT_Button, CT_SmallButton, CT_FillButton, CT_Selectable, CT_SelectableSpanRow };\n        static int contents_type = CT_SelectableSpanRow;\n        const char* contents_type_names[] = { \"Text\", \"Button\", \"SmallButton\", \"FillButton\", \"Selectable\", \"Selectable (span row)\" };\n        static int freeze_cols = 1;\n        static int freeze_rows = 1;\n        static int items_count = IM_ARRAYSIZE(template_items_names) * 2;\n        static ImVec2 outer_size_value = ImVec2(0.0f, TEXT_BASE_HEIGHT * 12);\n        static float row_min_height = 0.0f; // Auto\n        static float inner_width_with_scroll = 0.0f; // Auto-extend\n        static bool outer_size_enabled = true;\n        static bool show_headers = true;\n        static bool show_wrapped_text = false;\n        //static ImGuiTextFilter filter;\n        //ImGui::SetNextItemOpen(true, ImGuiCond_Once); // FIXME-TABLE: Enabling this results in initial clipped first pass on table which tend to affect column sizing\n        if (ImGui::TreeNode(\"Options\"))\n        {\n            // Make the UI compact because there are so many fields\n            PushStyleCompact();\n            ImGui::PushItemWidth(TEXT_BASE_WIDTH * 28.0f);\n\n            if (ImGui::TreeNodeEx(\"Features:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_Resizable\", &flags, ImGuiTableFlags_Resizable);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_Reorderable\", &flags, ImGuiTableFlags_Reorderable);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_Hideable\", &flags, ImGuiTableFlags_Hideable);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_Sortable\", &flags, ImGuiTableFlags_Sortable);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoSavedSettings\", &flags, ImGuiTableFlags_NoSavedSettings);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_ContextMenuInBody\", &flags, ImGuiTableFlags_ContextMenuInBody);\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Decorations:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_RowBg\", &flags, ImGuiTableFlags_RowBg);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersV\", &flags, ImGuiTableFlags_BordersV);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterV\", &flags, ImGuiTableFlags_BordersOuterV);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerV\", &flags, ImGuiTableFlags_BordersInnerV);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersH\", &flags, ImGuiTableFlags_BordersH);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersOuterH\", &flags, ImGuiTableFlags_BordersOuterH);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_BordersInnerH\", &flags, ImGuiTableFlags_BordersInnerH);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBody\", &flags, ImGuiTableFlags_NoBordersInBody); ImGui::SameLine(); HelpMarker(\"Disable vertical borders in columns Body (borders will always appear in Headers\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoBordersInBodyUntilResize\", &flags, ImGuiTableFlags_NoBordersInBodyUntilResize); ImGui::SameLine(); HelpMarker(\"Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers)\");\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Sizing:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                EditTableSizingFlags(&flags);\n                ImGui::SameLine(); HelpMarker(\"In the Advanced demo we override the policy of each column so those table-wide settings have less effect that typical.\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendX\", &flags, ImGuiTableFlags_NoHostExtendX);\n                ImGui::SameLine(); HelpMarker(\"Make outer width auto-fit to columns, overriding outer_size.x value.\\n\\nOnly available when ScrollX/ScrollY are disabled and Stretch columns are not used.\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoHostExtendY\", &flags, ImGuiTableFlags_NoHostExtendY);\n                ImGui::SameLine(); HelpMarker(\"Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).\\n\\nOnly available when ScrollX/ScrollY are disabled. Data below the limit will be clipped and not visible.\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoKeepColumnsVisible\", &flags, ImGuiTableFlags_NoKeepColumnsVisible);\n                ImGui::SameLine(); HelpMarker(\"Only available if ScrollX is disabled.\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_PreciseWidths\", &flags, ImGuiTableFlags_PreciseWidths);\n                ImGui::SameLine(); HelpMarker(\"Disable distributing remainder width to stretched columns (width allocation on a 100-wide table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33). With larger number of columns, resizing will appear to be less smooth.\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoClip\", &flags, ImGuiTableFlags_NoClip);\n                ImGui::SameLine(); HelpMarker(\"Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with ScrollFreeze options.\");\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Padding:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_PadOuterX\", &flags, ImGuiTableFlags_PadOuterX);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoPadOuterX\", &flags, ImGuiTableFlags_NoPadOuterX);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_NoPadInnerX\", &flags, ImGuiTableFlags_NoPadInnerX);\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Scrolling:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollX\", &flags, ImGuiTableFlags_ScrollX);\n                ImGui::SameLine();\n                ImGui::SetNextItemWidth(ImGui::GetFrameHeight());\n                ImGui::DragInt(\"freeze_cols\", &freeze_cols, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_ScrollY\", &flags, ImGuiTableFlags_ScrollY);\n                ImGui::SameLine();\n                ImGui::SetNextItemWidth(ImGui::GetFrameHeight());\n                ImGui::DragInt(\"freeze_rows\", &freeze_rows, 0.2f, 0, 9, NULL, ImGuiSliderFlags_NoInput);\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Sorting:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_SortMulti\", &flags, ImGuiTableFlags_SortMulti);\n                ImGui::SameLine(); HelpMarker(\"When sorting is enabled: hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1).\");\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_SortTristate\", &flags, ImGuiTableFlags_SortTristate);\n                ImGui::SameLine(); HelpMarker(\"When sorting is enabled: allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0).\");\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Headers:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::Checkbox(\"show_headers\", &show_headers);\n                ImGui::CheckboxFlags(\"ImGuiTableFlags_HighlightHoveredColumn\", &flags, ImGuiTableFlags_HighlightHoveredColumn);\n                ImGui::CheckboxFlags(\"ImGuiTableColumnFlags_AngledHeader\", &columns_base_flags, ImGuiTableColumnFlags_AngledHeader);\n                ImGui::SameLine(); HelpMarker(\"Enable AngledHeader on all columns. Best enabled on selected narrow columns (see \\\"Angled headers\\\" section of the demo).\");\n                ImGui::TreePop();\n            }\n\n            if (ImGui::TreeNodeEx(\"Other:\", ImGuiTreeNodeFlags_DefaultOpen))\n            {\n                ImGui::Checkbox(\"show_wrapped_text\", &show_wrapped_text);\n\n                ImGui::DragFloat2(\"##OuterSize\", &outer_size_value.x);\n                ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);\n                ImGui::Checkbox(\"outer_size\", &outer_size_enabled);\n                ImGui::SameLine();\n                HelpMarker(\"If scrolling is disabled (ScrollX and ScrollY not set):\\n\"\n                    \"- The table is output directly in the parent window.\\n\"\n                    \"- OuterSize.x < 0.0f will right-align the table.\\n\"\n                    \"- OuterSize.x = 0.0f will narrow fit the table unless there are any Stretch columns.\\n\"\n                    \"- OuterSize.y then becomes the minimum size for the table, which will extend vertically if there are more rows (unless NoHostExtendY is set).\");\n\n                // From a user point of view we will tend to use 'inner_width' differently depending on whether our table is embedding scrolling.\n                // To facilitate toying with this demo we will actually pass 0.0f to the BeginTable() when ScrollX is disabled.\n                ImGui::DragFloat(\"inner_width (when ScrollX active)\", &inner_width_with_scroll, 1.0f, 0.0f, FLT_MAX);\n\n                ImGui::DragFloat(\"row_min_height\", &row_min_height, 1.0f, 0.0f, FLT_MAX);\n                ImGui::SameLine(); HelpMarker(\"Specify height of the Selectable item.\");\n\n                ImGui::DragInt(\"items_count\", &items_count, 0.1f, 0, 9999);\n                ImGui::Combo(\"items_type (first column)\", &contents_type, contents_type_names, IM_ARRAYSIZE(contents_type_names));\n                //filter.Draw(\"filter\");\n                ImGui::TreePop();\n            }\n\n            ImGui::PopItemWidth();\n            PopStyleCompact();\n            ImGui::Spacing();\n            ImGui::TreePop();\n        }\n\n        // Update item list if we changed the number of items\n        static ImVector<MyItem> items;\n        static ImVector<int> selection;\n        static bool items_need_sort = false;\n        if (items.Size != items_count)\n        {\n            items.resize(items_count, MyItem());\n            for (int n = 0; n < items_count; n++)\n            {\n                const int template_n = n % IM_ARRAYSIZE(template_items_names);\n                MyItem& item = items[n];\n                item.ID = n;\n                item.Name = template_items_names[template_n];\n                item.Quantity = (template_n == 3) ? 10 : (template_n == 4) ? 20 : 0; // Assign default quantities\n            }\n        }\n\n        const ImDrawList* parent_draw_list = ImGui::GetWindowDrawList();\n        const int parent_draw_list_draw_cmd_count = parent_draw_list->CmdBuffer.Size;\n        ImVec2 table_scroll_cur, table_scroll_max; // For debug display\n        const ImDrawList* table_draw_list = NULL;  // \"\n\n        // Submit table\n        const float inner_width_to_use = (flags & ImGuiTableFlags_ScrollX) ? inner_width_with_scroll : 0.0f;\n        if (ImGui::BeginTable(\"table_advanced\", 6, flags, outer_size_enabled ? outer_size_value : ImVec2(0, 0), inner_width_to_use))\n        {\n            // Declare columns\n            // We use the \"user_id\" parameter of TableSetupColumn() to specify a user id that will be stored in the sort specifications.\n            // This is so our sort function can identify a column given our own identifier. We could also identify them based on their index!\n            ImGui::TableSetupColumn(\"ID\",           columns_base_flags | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHide, 0.0f, MyItemColumnID_ID);\n            ImGui::TableSetupColumn(\"Name\",         columns_base_flags | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Name);\n            ImGui::TableSetupColumn(\"Action\",       columns_base_flags | ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthFixed, 0.0f, MyItemColumnID_Action);\n            ImGui::TableSetupColumn(\"Quantity\",     columns_base_flags | ImGuiTableColumnFlags_PreferSortDescending, 0.0f, MyItemColumnID_Quantity);\n            ImGui::TableSetupColumn(\"Description\",  columns_base_flags | ((flags & ImGuiTableFlags_NoHostExtendX) ? 0 : ImGuiTableColumnFlags_WidthStretch), 0.0f, MyItemColumnID_Description);\n            ImGui::TableSetupColumn(\"Hidden\",       columns_base_flags |  ImGuiTableColumnFlags_DefaultHide | ImGuiTableColumnFlags_NoSort);\n            ImGui::TableSetupScrollFreeze(freeze_cols, freeze_rows);\n\n            // Sort our data if sort specs have been changed!\n            ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs();\n            if (sort_specs && sort_specs->SpecsDirty)\n                items_need_sort = true;\n            if (sort_specs && items_need_sort && items.Size > 1)\n            {\n                MyItem::SortWithSortSpecs(sort_specs, items.Data, items.Size);\n                sort_specs->SpecsDirty = false;\n            }\n            items_need_sort = false;\n\n            // Take note of whether we are currently sorting based on the Quantity field,\n            // we will use this to trigger sorting when we know the data of this column has been modified.\n            const bool sorts_specs_using_quantity = (ImGui::TableGetColumnFlags(3) & ImGuiTableColumnFlags_IsSorted) != 0;\n\n            // Show headers\n            if (show_headers && (columns_base_flags & ImGuiTableColumnFlags_AngledHeader) != 0)\n                ImGui::TableAngledHeadersRow();\n            if (show_headers)\n                ImGui::TableHeadersRow();\n\n            // Show data\n            // FIXME-TABLE FIXME-NAV: How we can get decent up/down even though we have the buttons here?\n            ImGui::PushButtonRepeat(true);\n#if 1\n            // Demonstrate using clipper for large vertical lists\n            ImGuiListClipper clipper;\n            clipper.Begin(items.Size);\n            while (clipper.Step())\n            {\n                for (int row_n = clipper.DisplayStart; row_n < clipper.DisplayEnd; row_n++)\n#else\n            // Without clipper\n            {\n                for (int row_n = 0; row_n < items.Size; row_n++)\n#endif\n                {\n                    MyItem* item = &items[row_n];\n                    //if (!filter.PassFilter(item->Name))\n                    //    continue;\n\n                    const bool item_is_selected = selection.contains(item->ID);\n                    ImGui::PushID(item->ID);\n                    ImGui::TableNextRow(ImGuiTableRowFlags_None, row_min_height);\n\n                    // For the demo purpose we can select among different type of items submitted in the first column\n                    ImGui::TableSetColumnIndex(0);\n                    char label[32];\n                    sprintf(label, \"%04d\", item->ID);\n                    if (contents_type == CT_Text)\n                        ImGui::TextUnformatted(label);\n                    else if (contents_type == CT_Button)\n                        ImGui::Button(label);\n                    else if (contents_type == CT_SmallButton)\n                        ImGui::SmallButton(label);\n                    else if (contents_type == CT_FillButton)\n                        ImGui::Button(label, ImVec2(-FLT_MIN, 0.0f));\n                    else if (contents_type == CT_Selectable || contents_type == CT_SelectableSpanRow)\n                    {\n                        ImGuiSelectableFlags selectable_flags = (contents_type == CT_SelectableSpanRow) ? ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap : ImGuiSelectableFlags_None;\n                        if (ImGui::Selectable(label, item_is_selected, selectable_flags, ImVec2(0, row_min_height)))\n                        {\n                            if (ImGui::GetIO().KeyCtrl)\n                            {\n                                if (item_is_selected)\n                                    selection.find_erase_unsorted(item->ID);\n                                else\n                                    selection.push_back(item->ID);\n                            }\n                            else\n                            {\n                                selection.clear();\n                                selection.push_back(item->ID);\n                            }\n                        }\n                    }\n\n                    if (ImGui::TableSetColumnIndex(1))\n                        ImGui::TextUnformatted(item->Name);\n\n                    // Here we demonstrate marking our data set as needing to be sorted again if we modified a quantity,\n                    // and we are currently sorting on the column showing the Quantity.\n                    // To avoid triggering a sort while holding the button, we only trigger it when the button has been released.\n                    // You will probably need a more advanced system in your code if you want to automatically sort when a specific entry changes.\n                    if (ImGui::TableSetColumnIndex(2))\n                    {\n                        if (ImGui::SmallButton(\"Chop\")) { item->Quantity += 1; }\n                        if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }\n                        ImGui::SameLine();\n                        if (ImGui::SmallButton(\"Eat\")) { item->Quantity -= 1; }\n                        if (sorts_specs_using_quantity && ImGui::IsItemDeactivated()) { items_need_sort = true; }\n                    }\n\n                    if (ImGui::TableSetColumnIndex(3))\n                        ImGui::Text(\"%d\", item->Quantity);\n\n                    ImGui::TableSetColumnIndex(4);\n                    if (show_wrapped_text)\n                        ImGui::TextWrapped(\"Lorem ipsum dolor sit amet\");\n                    else\n                        ImGui::Text(\"Lorem ipsum dolor sit amet\");\n\n                    if (ImGui::TableSetColumnIndex(5))\n                        ImGui::Text(\"1234\");\n\n                    ImGui::PopID();\n                }\n            }\n            ImGui::PopButtonRepeat();\n\n            // Store some info to display debug details below\n            table_scroll_cur = ImVec2(ImGui::GetScrollX(), ImGui::GetScrollY());\n            table_scroll_max = ImVec2(ImGui::GetScrollMaxX(), ImGui::GetScrollMaxY());\n            table_draw_list = ImGui::GetWindowDrawList();\n            ImGui::EndTable();\n        }\n        static bool show_debug_details = false;\n        ImGui::Checkbox(\"Debug details\", &show_debug_details);\n        if (show_debug_details && table_draw_list)\n        {\n            ImGui::SameLine(0.0f, 0.0f);\n            const int table_draw_list_draw_cmd_count = table_draw_list->CmdBuffer.Size;\n            if (table_draw_list == parent_draw_list)\n                ImGui::Text(\": DrawCmd: +%d (in same window)\",\n                    table_draw_list_draw_cmd_count - parent_draw_list_draw_cmd_count);\n            else\n                ImGui::Text(\": DrawCmd: +%d (in child window), Scroll: (%.f/%.f) (%.f/%.f)\",\n                    table_draw_list_draw_cmd_count - 1, table_scroll_cur.x, table_scroll_max.x, table_scroll_cur.y, table_scroll_max.y);\n        }\n        ImGui::TreePop();\n    }\n\n    ImGui::PopID();\n\n    ShowDemoWindowColumns();\n\n    if (disable_indent)\n        ImGui::PopStyleVar();\n}\n\n// Demonstrate old/legacy Columns API!\n// [2020: Columns are under-featured and not maintained. Prefer using the more flexible and powerful BeginTable() API!]\nstatic void ShowDemoWindowColumns()\n{\n    IMGUI_DEMO_MARKER(\"Columns (legacy API)\");\n    bool open = ImGui::TreeNode(\"Legacy Columns API\");\n    ImGui::SameLine();\n    HelpMarker(\"Columns() is an old API! Prefer using the more flexible and powerful BeginTable() API!\");\n    if (!open)\n        return;\n\n    // Basic columns\n    IMGUI_DEMO_MARKER(\"Columns (legacy API)/Basic\");\n    if (ImGui::TreeNode(\"Basic\"))\n    {\n        ImGui::Text(\"Without border:\");\n        ImGui::Columns(3, \"mycolumns3\", false);  // 3-ways, no border\n        ImGui::Separator();\n        for (int n = 0; n < 14; n++)\n        {\n            char label[32];\n            sprintf(label, \"Item %d\", n);\n            if (ImGui::Selectable(label)) {}\n            //if (ImGui::Button(label, ImVec2(-FLT_MIN,0.0f))) {}\n            ImGui::NextColumn();\n        }\n        ImGui::Columns(1);\n        ImGui::Separator();\n\n        ImGui::Text(\"With border:\");\n        ImGui::Columns(4, \"mycolumns\"); // 4-ways, with border\n        ImGui::Separator();\n        ImGui::Text(\"ID\"); ImGui::NextColumn();\n        ImGui::Text(\"Name\"); ImGui::NextColumn();\n        ImGui::Text(\"Path\"); ImGui::NextColumn();\n        ImGui::Text(\"Hovered\"); ImGui::NextColumn();\n        ImGui::Separator();\n        const char* names[3] = { \"One\", \"Two\", \"Three\" };\n        const char* paths[3] = { \"/path/one\", \"/path/two\", \"/path/three\" };\n        static int selected = -1;\n        for (int i = 0; i < 3; i++)\n        {\n            char label[32];\n            sprintf(label, \"%04d\", i);\n            if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns))\n                selected = i;\n            bool hovered = ImGui::IsItemHovered();\n            ImGui::NextColumn();\n            ImGui::Text(names[i]); ImGui::NextColumn();\n            ImGui::Text(paths[i]); ImGui::NextColumn();\n            ImGui::Text(\"%d\", hovered); ImGui::NextColumn();\n        }\n        ImGui::Columns(1);\n        ImGui::Separator();\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Columns (legacy API)/Borders\");\n    if (ImGui::TreeNode(\"Borders\"))\n    {\n        // NB: Future columns API should allow automatic horizontal borders.\n        static bool h_borders = true;\n        static bool v_borders = true;\n        static int columns_count = 4;\n        const int lines_count = 3;\n        ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);\n        ImGui::DragInt(\"##columns_count\", &columns_count, 0.1f, 2, 10, \"%d columns\");\n        if (columns_count < 2)\n            columns_count = 2;\n        ImGui::SameLine();\n        ImGui::Checkbox(\"horizontal\", &h_borders);\n        ImGui::SameLine();\n        ImGui::Checkbox(\"vertical\", &v_borders);\n        ImGui::Columns(columns_count, NULL, v_borders);\n        for (int i = 0; i < columns_count * lines_count; i++)\n        {\n            if (h_borders && ImGui::GetColumnIndex() == 0)\n                ImGui::Separator();\n            ImGui::Text(\"%c%c%c\", 'a' + i, 'a' + i, 'a' + i);\n            ImGui::Text(\"Width %.2f\", ImGui::GetColumnWidth());\n            ImGui::Text(\"Avail %.2f\", ImGui::GetContentRegionAvail().x);\n            ImGui::Text(\"Offset %.2f\", ImGui::GetColumnOffset());\n            ImGui::Text(\"Long text that is likely to clip\");\n            ImGui::Button(\"Button\", ImVec2(-FLT_MIN, 0.0f));\n            ImGui::NextColumn();\n        }\n        ImGui::Columns(1);\n        if (h_borders)\n            ImGui::Separator();\n        ImGui::TreePop();\n    }\n\n    // Create multiple items in a same cell before switching to next column\n    IMGUI_DEMO_MARKER(\"Columns (legacy API)/Mixed items\");\n    if (ImGui::TreeNode(\"Mixed items\"))\n    {\n        ImGui::Columns(3, \"mixed\");\n        ImGui::Separator();\n\n        ImGui::Text(\"Hello\");\n        ImGui::Button(\"Banana\");\n        ImGui::NextColumn();\n\n        ImGui::Text(\"ImGui\");\n        ImGui::Button(\"Apple\");\n        static float foo = 1.0f;\n        ImGui::InputFloat(\"red\", &foo, 0.05f, 0, \"%.3f\");\n        ImGui::Text(\"An extra line here.\");\n        ImGui::NextColumn();\n\n        ImGui::Text(\"Sailor\");\n        ImGui::Button(\"Corniflower\");\n        static float bar = 1.0f;\n        ImGui::InputFloat(\"blue\", &bar, 0.05f, 0, \"%.3f\");\n        ImGui::NextColumn();\n\n        if (ImGui::CollapsingHeader(\"Category A\")) { ImGui::Text(\"Blah blah blah\"); } ImGui::NextColumn();\n        if (ImGui::CollapsingHeader(\"Category B\")) { ImGui::Text(\"Blah blah blah\"); } ImGui::NextColumn();\n        if (ImGui::CollapsingHeader(\"Category C\")) { ImGui::Text(\"Blah blah blah\"); } ImGui::NextColumn();\n        ImGui::Columns(1);\n        ImGui::Separator();\n        ImGui::TreePop();\n    }\n\n    // Word wrapping\n    IMGUI_DEMO_MARKER(\"Columns (legacy API)/Word-wrapping\");\n    if (ImGui::TreeNode(\"Word-wrapping\"))\n    {\n        ImGui::Columns(2, \"word-wrapping\");\n        ImGui::Separator();\n        ImGui::TextWrapped(\"The quick brown fox jumps over the lazy dog.\");\n        ImGui::TextWrapped(\"Hello Left\");\n        ImGui::NextColumn();\n        ImGui::TextWrapped(\"The quick brown fox jumps over the lazy dog.\");\n        ImGui::TextWrapped(\"Hello Right\");\n        ImGui::Columns(1);\n        ImGui::Separator();\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Columns (legacy API)/Horizontal Scrolling\");\n    if (ImGui::TreeNode(\"Horizontal Scrolling\"))\n    {\n        ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f));\n        ImVec2 child_size = ImVec2(0, ImGui::GetFontSize() * 20.0f);\n        ImGui::BeginChild(\"##ScrollingRegion\", child_size, ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar);\n        ImGui::Columns(10);\n\n        // Also demonstrate using clipper for large vertical lists\n        int ITEMS_COUNT = 2000;\n        ImGuiListClipper clipper;\n        clipper.Begin(ITEMS_COUNT);\n        while (clipper.Step())\n        {\n            for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)\n                for (int j = 0; j < 10; j++)\n                {\n                    ImGui::Text(\"Line %d Column %d...\", i, j);\n                    ImGui::NextColumn();\n                }\n        }\n        ImGui::Columns(1);\n        ImGui::EndChild();\n        ImGui::TreePop();\n    }\n\n    IMGUI_DEMO_MARKER(\"Columns (legacy API)/Tree\");\n    if (ImGui::TreeNode(\"Tree\"))\n    {\n        ImGui::Columns(2, \"tree\", true);\n        for (int x = 0; x < 3; x++)\n        {\n            bool open1 = ImGui::TreeNode((void*)(intptr_t)x, \"Node%d\", x);\n            ImGui::NextColumn();\n            ImGui::Text(\"Node contents\");\n            ImGui::NextColumn();\n            if (open1)\n            {\n                for (int y = 0; y < 3; y++)\n                {\n                    bool open2 = ImGui::TreeNode((void*)(intptr_t)y, \"Node%d.%d\", x, y);\n                    ImGui::NextColumn();\n                    ImGui::Text(\"Node contents\");\n                    if (open2)\n                    {\n                        ImGui::Text(\"Even more contents\");\n                        if (ImGui::TreeNode(\"Tree in column\"))\n                        {\n                            ImGui::Text(\"The quick brown fox jumps over the lazy dog\");\n                            ImGui::TreePop();\n                        }\n                    }\n                    ImGui::NextColumn();\n                    if (open2)\n                        ImGui::TreePop();\n                }\n                ImGui::TreePop();\n            }\n        }\n        ImGui::Columns(1);\n        ImGui::TreePop();\n    }\n\n    ImGui::TreePop();\n}\n\nstatic void ShowDemoWindowInputs()\n{\n    IMGUI_DEMO_MARKER(\"Inputs & Focus\");\n    if (ImGui::CollapsingHeader(\"Inputs & Focus\"))\n    {\n        ImGuiIO& io = ImGui::GetIO();\n\n        // Display inputs submitted to ImGuiIO\n        IMGUI_DEMO_MARKER(\"Inputs & Focus/Inputs\");\n        ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n        if (ImGui::TreeNode(\"Inputs\"))\n        {\n            HelpMarker(\n                \"This is a simplified view. See more detailed input state:\\n\"\n                \"- in 'Tools->Metrics/Debugger->Inputs'.\\n\"\n                \"- in 'Tools->Debug Log->IO'.\");\n            if (ImGui::IsMousePosValid())\n                ImGui::Text(\"Mouse pos: (%g, %g)\", io.MousePos.x, io.MousePos.y);\n            else\n                ImGui::Text(\"Mouse pos: <INVALID>\");\n            ImGui::Text(\"Mouse delta: (%g, %g)\", io.MouseDelta.x, io.MouseDelta.y);\n            ImGui::Text(\"Mouse down:\");\n            for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text(\"b%d (%.02f secs)\", i, io.MouseDownDuration[i]); }\n            ImGui::Text(\"Mouse wheel: %.1f\", io.MouseWheel);\n\n            // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allows displaying the data for old/new backends.\n            // User code should never have to go through such hoops! You can generally iterate between ImGuiKey_NamedKey_BEGIN and ImGuiKey_NamedKey_END.\n#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO\n            struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } };\n            ImGuiKey start_key = ImGuiKey_NamedKey_BEGIN;\n#else\n            struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && ImGui::GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array\n            ImGuiKey start_key = (ImGuiKey)0;\n#endif\n            ImGui::Text(\"Keys down:\");         for (ImGuiKey key = start_key; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !ImGui::IsKeyDown(key)) continue; ImGui::SameLine(); ImGui::Text((key < ImGuiKey_NamedKey_BEGIN) ? \"\\\"%s\\\"\" : \"\\\"%s\\\" %d\", ImGui::GetKeyName(key), key); }\n            ImGui::Text(\"Keys mods: %s%s%s%s\", io.KeyCtrl ? \"CTRL \" : \"\", io.KeyShift ? \"SHIFT \" : \"\", io.KeyAlt ? \"ALT \" : \"\", io.KeySuper ? \"SUPER \" : \"\");\n            ImGui::Text(\"Chars queue:\");       for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; ImGui::SameLine();  ImGui::Text(\"\\'%c\\' (0x%04X)\", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public.\n\n            ImGui::TreePop();\n        }\n\n        // Display ImGuiIO output flags\n        IMGUI_DEMO_MARKER(\"Inputs & Focus/Outputs\");\n        ImGui::SetNextItemOpen(true, ImGuiCond_Once);\n        if (ImGui::TreeNode(\"Outputs\"))\n        {\n            HelpMarker(\n                \"The value of io.WantCaptureMouse and io.WantCaptureKeyboard are normally set by Dear ImGui \"\n                \"to instruct your application of how to route inputs. Typically, when a value is true, it means \"\n                \"Dear ImGui wants the corresponding inputs and we expect the underlying application to ignore them.\\n\\n\"\n                \"The most typical case is: when hovering a window, Dear ImGui set io.WantCaptureMouse to true, \"\n                \"and underlying application should ignore mouse inputs (in practice there are many and more subtle \"\n                \"rules leading to how those flags are set).\");\n            ImGui::Text(\"io.WantCaptureMouse: %d\", io.WantCaptureMouse);\n            ImGui::Text(\"io.WantCaptureMouseUnlessPopupClose: %d\", io.WantCaptureMouseUnlessPopupClose);\n            ImGui::Text(\"io.WantCaptureKeyboard: %d\", io.WantCaptureKeyboard);\n            ImGui::Text(\"io.WantTextInput: %d\", io.WantTextInput);\n            ImGui::Text(\"io.WantSetMousePos: %d\", io.WantSetMousePos);\n            ImGui::Text(\"io.NavActive: %d, io.NavVisible: %d\", io.NavActive, io.NavVisible);\n\n            IMGUI_DEMO_MARKER(\"Inputs & Focus/Outputs/WantCapture override\");\n            if (ImGui::TreeNode(\"WantCapture override\"))\n            {\n                HelpMarker(\n                    \"Hovering the colored canvas will override io.WantCaptureXXX fields.\\n\"\n                    \"Notice how normally (when set to none), the value of io.WantCaptureKeyboard would be false when hovering and true when clicking.\");\n                static int capture_override_mouse = -1;\n                static int capture_override_keyboard = -1;\n                const char* capture_override_desc[] = { \"None\", \"Set to false\", \"Set to true\" };\n                ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);\n                ImGui::SliderInt(\"SetNextFrameWantCaptureMouse() on hover\", &capture_override_mouse, -1, +1, capture_override_desc[capture_override_mouse + 1], ImGuiSliderFlags_AlwaysClamp);\n                ImGui::SetNextItemWidth(ImGui::GetFontSize() * 15);\n                ImGui::SliderInt(\"SetNextFrameWantCaptureKeyboard() on hover\", &capture_override_keyboard, -1, +1, capture_override_desc[capture_override_keyboard + 1], ImGuiSliderFlags_AlwaysClamp);\n\n                ImGui::ColorButton(\"##panel\", ImVec4(0.7f, 0.1f, 0.7f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, ImVec2(128.0f, 96.0f)); // Dummy item\n                if (ImGui::IsItemHovered() && capture_override_mouse != -1)\n                    ImGui::SetNextFrameWantCaptureMouse(capture_override_mouse == 1);\n                if (ImGui::IsItemHovered() && capture_override_keyboard != -1)\n                    ImGui::SetNextFrameWantCaptureKeyboard(capture_override_keyboard == 1);\n\n                ImGui::TreePop();\n            }\n            ImGui::TreePop();\n        }\n\n        // Display mouse cursors\n        IMGUI_DEMO_MARKER(\"Inputs & Focus/Mouse Cursors\");\n        if (ImGui::TreeNode(\"Mouse Cursors\"))\n        {\n            const char* mouse_cursors_names[] = { \"Arrow\", \"TextInput\", \"ResizeAll\", \"ResizeNS\", \"ResizeEW\", \"ResizeNESW\", \"ResizeNWSE\", \"Hand\", \"NotAllowed\" };\n            IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT);\n\n            ImGuiMouseCursor current = ImGui::GetMouseCursor();\n            ImGui::Text(\"Current mouse cursor = %d: %s\", current, mouse_cursors_names[current]);\n            ImGui::BeginDisabled(true);\n            ImGui::CheckboxFlags(\"io.BackendFlags: HasMouseCursors\", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors);\n            ImGui::EndDisabled();\n\n            ImGui::Text(\"Hover to see mouse cursors:\");\n            ImGui::SameLine(); HelpMarker(\n                \"Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. \"\n                \"If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, \"\n                \"otherwise your backend needs to handle it.\");\n            for (int i = 0; i < ImGuiMouseCursor_COUNT; i++)\n            {\n                char label[32];\n                sprintf(label, \"Mouse cursor %d: %s\", i, mouse_cursors_names[i]);\n                ImGui::Bullet(); ImGui::Selectable(label, false);\n                if (ImGui::IsItemHovered())\n                    ImGui::SetMouseCursor(i);\n            }\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Inputs & Focus/Tabbing\");\n        if (ImGui::TreeNode(\"Tabbing\"))\n        {\n            ImGui::Text(\"Use TAB/SHIFT+TAB to cycle through keyboard editable fields.\");\n            static char buf[32] = \"hello\";\n            ImGui::InputText(\"1\", buf, IM_ARRAYSIZE(buf));\n            ImGui::InputText(\"2\", buf, IM_ARRAYSIZE(buf));\n            ImGui::InputText(\"3\", buf, IM_ARRAYSIZE(buf));\n            ImGui::PushTabStop(false);\n            ImGui::InputText(\"4 (tab skip)\", buf, IM_ARRAYSIZE(buf));\n            ImGui::SameLine(); HelpMarker(\"Item won't be cycled through when using TAB or Shift+Tab.\");\n            ImGui::PopTabStop();\n            ImGui::InputText(\"5\", buf, IM_ARRAYSIZE(buf));\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Inputs & Focus/Focus from code\");\n        if (ImGui::TreeNode(\"Focus from code\"))\n        {\n            bool focus_1 = ImGui::Button(\"Focus on 1\"); ImGui::SameLine();\n            bool focus_2 = ImGui::Button(\"Focus on 2\"); ImGui::SameLine();\n            bool focus_3 = ImGui::Button(\"Focus on 3\");\n            int has_focus = 0;\n            static char buf[128] = \"click on a button to set focus\";\n\n            if (focus_1) ImGui::SetKeyboardFocusHere();\n            ImGui::InputText(\"1\", buf, IM_ARRAYSIZE(buf));\n            if (ImGui::IsItemActive()) has_focus = 1;\n\n            if (focus_2) ImGui::SetKeyboardFocusHere();\n            ImGui::InputText(\"2\", buf, IM_ARRAYSIZE(buf));\n            if (ImGui::IsItemActive()) has_focus = 2;\n\n            ImGui::PushTabStop(false);\n            if (focus_3) ImGui::SetKeyboardFocusHere();\n            ImGui::InputText(\"3 (tab skip)\", buf, IM_ARRAYSIZE(buf));\n            if (ImGui::IsItemActive()) has_focus = 3;\n            ImGui::SameLine(); HelpMarker(\"Item won't be cycled through when using TAB or Shift+Tab.\");\n            ImGui::PopTabStop();\n\n            if (has_focus)\n                ImGui::Text(\"Item with focus: %d\", has_focus);\n            else\n                ImGui::Text(\"Item with focus: <none>\");\n\n            // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item\n            static float f3[3] = { 0.0f, 0.0f, 0.0f };\n            int focus_ahead = -1;\n            if (ImGui::Button(\"Focus on X\")) { focus_ahead = 0; } ImGui::SameLine();\n            if (ImGui::Button(\"Focus on Y\")) { focus_ahead = 1; } ImGui::SameLine();\n            if (ImGui::Button(\"Focus on Z\")) { focus_ahead = 2; }\n            if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead);\n            ImGui::SliderFloat3(\"Float3\", &f3[0], 0.0f, 1.0f);\n\n            ImGui::TextWrapped(\"NB: Cursor & selection are preserved when refocusing last used item in code.\");\n            ImGui::TreePop();\n        }\n\n        IMGUI_DEMO_MARKER(\"Inputs & Focus/Dragging\");\n        if (ImGui::TreeNode(\"Dragging\"))\n        {\n            ImGui::TextWrapped(\"You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.\");\n            for (int button = 0; button < 3; button++)\n            {\n                ImGui::Text(\"IsMouseDragging(%d):\", button);\n                ImGui::Text(\"  w/ default threshold: %d,\", ImGui::IsMouseDragging(button));\n                ImGui::Text(\"  w/ zero threshold: %d,\", ImGui::IsMouseDragging(button, 0.0f));\n                ImGui::Text(\"  w/ large threshold: %d,\", ImGui::IsMouseDragging(button, 20.0f));\n            }\n\n            ImGui::Button(\"Drag Me\");\n            if (ImGui::IsItemActive())\n                ImGui::GetForegroundDrawList()->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); // Draw a line between the button and the mouse cursor\n\n            // Drag operations gets \"unlocked\" when the mouse has moved past a certain threshold\n            // (the default threshold is stored in io.MouseDragThreshold). You can request a lower or higher\n            // threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta().\n            ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f);\n            ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0);\n            ImVec2 mouse_delta = io.MouseDelta;\n            ImGui::Text(\"GetMouseDragDelta(0):\");\n            ImGui::Text(\"  w/ default threshold: (%.1f, %.1f)\", value_with_lock_threshold.x, value_with_lock_threshold.y);\n            ImGui::Text(\"  w/ zero threshold: (%.1f, %.1f)\", value_raw.x, value_raw.y);\n            ImGui::Text(\"io.MouseDelta: (%.1f, %.1f)\", mouse_delta.x, mouse_delta.y);\n            ImGui::TreePop();\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] About Window / ShowAboutWindow()\n// Access from Dear ImGui Demo -> Tools -> About\n//-----------------------------------------------------------------------------\n\nvoid ImGui::ShowAboutWindow(bool* p_open)\n{\n    if (!ImGui::Begin(\"About Dear ImGui\", p_open, ImGuiWindowFlags_AlwaysAutoResize))\n    {\n        ImGui::End();\n        return;\n    }\n    IMGUI_DEMO_MARKER(\"Tools/About Dear ImGui\");\n    ImGui::Text(\"Dear ImGui %s (%d)\", IMGUI_VERSION, IMGUI_VERSION_NUM);\n    ImGui::Separator();\n    ImGui::Text(\"By Omar Cornut and all Dear ImGui contributors.\");\n    ImGui::Text(\"Dear ImGui is licensed under the MIT License, see LICENSE for more information.\");\n    ImGui::Text(\"If your company uses this, please consider sponsoring the project!\");\n\n    static bool show_config_info = false;\n    ImGui::Checkbox(\"Config/Build Information\", &show_config_info);\n    if (show_config_info)\n    {\n        ImGuiIO& io = ImGui::GetIO();\n        ImGuiStyle& style = ImGui::GetStyle();\n\n        bool copy_to_clipboard = ImGui::Button(\"Copy to clipboard\");\n        ImVec2 child_size = ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 18);\n        ImGui::BeginChild(ImGui::GetID(\"cfg_infos\"), child_size, ImGuiChildFlags_FrameStyle);\n        if (copy_to_clipboard)\n        {\n            ImGui::LogToClipboard();\n            ImGui::LogText(\"```\\n\"); // Back quotes will make text appears without formatting when pasting on GitHub\n        }\n\n        ImGui::Text(\"Dear ImGui %s (%d)\", IMGUI_VERSION, IMGUI_VERSION_NUM);\n        ImGui::Separator();\n        ImGui::Text(\"sizeof(size_t): %d, sizeof(ImDrawIdx): %d, sizeof(ImDrawVert): %d\", (int)sizeof(size_t), (int)sizeof(ImDrawIdx), (int)sizeof(ImDrawVert));\n        ImGui::Text(\"define: __cplusplus=%d\", (int)__cplusplus);\n#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_OBSOLETE_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO\n        ImGui::Text(\"define: IMGUI_DISABLE_OBSOLETE_KEYIO\");\n#endif\n#ifdef IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_WIN32_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_WIN32_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_FILE_FUNCTIONS\n        ImGui::Text(\"define: IMGUI_DISABLE_FILE_FUNCTIONS\");\n#endif\n#ifdef IMGUI_DISABLE_DEFAULT_ALLOCATORS\n        ImGui::Text(\"define: IMGUI_DISABLE_DEFAULT_ALLOCATORS\");\n#endif\n#ifdef IMGUI_USE_BGRA_PACKED_COLOR\n        ImGui::Text(\"define: IMGUI_USE_BGRA_PACKED_COLOR\");\n#endif\n#ifdef _WIN32\n        ImGui::Text(\"define: _WIN32\");\n#endif\n#ifdef _WIN64\n        ImGui::Text(\"define: _WIN64\");\n#endif\n#ifdef __linux__\n        ImGui::Text(\"define: __linux__\");\n#endif\n#ifdef __APPLE__\n        ImGui::Text(\"define: __APPLE__\");\n#endif\n#ifdef _MSC_VER\n        ImGui::Text(\"define: _MSC_VER=%d\", _MSC_VER);\n#endif\n#ifdef _MSVC_LANG\n        ImGui::Text(\"define: _MSVC_LANG=%d\", (int)_MSVC_LANG);\n#endif\n#ifdef __MINGW32__\n        ImGui::Text(\"define: __MINGW32__\");\n#endif\n#ifdef __MINGW64__\n        ImGui::Text(\"define: __MINGW64__\");\n#endif\n#ifdef __GNUC__\n        ImGui::Text(\"define: __GNUC__=%d\", (int)__GNUC__);\n#endif\n#ifdef __clang_version__\n        ImGui::Text(\"define: __clang_version__=%s\", __clang_version__);\n#endif\n#ifdef __EMSCRIPTEN__\n        ImGui::Text(\"define: __EMSCRIPTEN__\");\n#endif\n        ImGui::Separator();\n        ImGui::Text(\"io.BackendPlatformName: %s\", io.BackendPlatformName ? io.BackendPlatformName : \"NULL\");\n        ImGui::Text(\"io.BackendRendererName: %s\", io.BackendRendererName ? io.BackendRendererName : \"NULL\");\n        ImGui::Text(\"io.ConfigFlags: 0x%08X\", io.ConfigFlags);\n        if (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)        ImGui::Text(\" NavEnableKeyboard\");\n        if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad)         ImGui::Text(\" NavEnableGamepad\");\n        if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)     ImGui::Text(\" NavEnableSetMousePos\");\n        if (io.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)     ImGui::Text(\" NavNoCaptureKeyboard\");\n        if (io.ConfigFlags & ImGuiConfigFlags_NoMouse)                  ImGui::Text(\" NoMouse\");\n        if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)      ImGui::Text(\" NoMouseCursorChange\");\n        if (io.MouseDrawCursor)                                         ImGui::Text(\"io.MouseDrawCursor\");\n        if (io.ConfigMacOSXBehaviors)                                   ImGui::Text(\"io.ConfigMacOSXBehaviors\");\n        if (io.ConfigInputTextCursorBlink)                              ImGui::Text(\"io.ConfigInputTextCursorBlink\");\n        if (io.ConfigWindowsResizeFromEdges)                            ImGui::Text(\"io.ConfigWindowsResizeFromEdges\");\n        if (io.ConfigWindowsMoveFromTitleBarOnly)                       ImGui::Text(\"io.ConfigWindowsMoveFromTitleBarOnly\");\n        if (io.ConfigMemoryCompactTimer >= 0.0f)                        ImGui::Text(\"io.ConfigMemoryCompactTimer = %.1f\", io.ConfigMemoryCompactTimer);\n        ImGui::Text(\"io.BackendFlags: 0x%08X\", io.BackendFlags);\n        if (io.BackendFlags & ImGuiBackendFlags_HasGamepad)             ImGui::Text(\" HasGamepad\");\n        if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors)        ImGui::Text(\" HasMouseCursors\");\n        if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos)         ImGui::Text(\" HasSetMousePos\");\n        if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset)   ImGui::Text(\" RendererHasVtxOffset\");\n        ImGui::Separator();\n        ImGui::Text(\"io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d\", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight);\n        ImGui::Text(\"io.DisplaySize: %.2f,%.2f\", io.DisplaySize.x, io.DisplaySize.y);\n        ImGui::Text(\"io.DisplayFramebufferScale: %.2f,%.2f\", io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);\n        ImGui::Separator();\n        ImGui::Text(\"style.WindowPadding: %.2f,%.2f\", style.WindowPadding.x, style.WindowPadding.y);\n        ImGui::Text(\"style.WindowBorderSize: %.2f\", style.WindowBorderSize);\n        ImGui::Text(\"style.FramePadding: %.2f,%.2f\", style.FramePadding.x, style.FramePadding.y);\n        ImGui::Text(\"style.FrameRounding: %.2f\", style.FrameRounding);\n        ImGui::Text(\"style.FrameBorderSize: %.2f\", style.FrameBorderSize);\n        ImGui::Text(\"style.ItemSpacing: %.2f,%.2f\", style.ItemSpacing.x, style.ItemSpacing.y);\n        ImGui::Text(\"style.ItemInnerSpacing: %.2f,%.2f\", style.ItemInnerSpacing.x, style.ItemInnerSpacing.y);\n\n        if (copy_to_clipboard)\n        {\n            ImGui::LogText(\"\\n```\\n\");\n            ImGui::LogFinish();\n        }\n        ImGui::EndChild();\n    }\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Style Editor / ShowStyleEditor()\n//-----------------------------------------------------------------------------\n// - ShowFontSelector()\n// - ShowStyleSelector()\n// - ShowStyleEditor()\n//-----------------------------------------------------------------------------\n\n// Forward declare ShowFontAtlas() which isn't worth putting in public API yet\nnamespace ImGui { IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); }\n\n// Demo helper function to select among loaded fonts.\n// Here we use the regular BeginCombo()/EndCombo() api which is the more flexible one.\nvoid ImGui::ShowFontSelector(const char* label)\n{\n    ImGuiIO& io = ImGui::GetIO();\n    ImFont* font_current = ImGui::GetFont();\n    if (ImGui::BeginCombo(label, font_current->GetDebugName()))\n    {\n        for (ImFont* font : io.Fonts->Fonts)\n        {\n            ImGui::PushID((void*)font);\n            if (ImGui::Selectable(font->GetDebugName(), font == font_current))\n                io.FontDefault = font;\n            ImGui::PopID();\n        }\n        ImGui::EndCombo();\n    }\n    ImGui::SameLine();\n    HelpMarker(\n        \"- Load additional fonts with io.Fonts->AddFontFromFileTTF().\\n\"\n        \"- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\\n\"\n        \"- Read FAQ and docs/FONTS.md for more details.\\n\"\n        \"- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame().\");\n}\n\n// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options.\n// Here we use the simplified Combo() api that packs items into a single literal string.\n// Useful for quick combo boxes where the choices are known locally.\nbool ImGui::ShowStyleSelector(const char* label)\n{\n    static int style_idx = -1;\n    if (ImGui::Combo(label, &style_idx, \"Dark\\0Light\\0Classic\\0\"))\n    {\n        switch (style_idx)\n        {\n        case 0: ImGui::StyleColorsDark(); break;\n        case 1: ImGui::StyleColorsLight(); break;\n        case 2: ImGui::StyleColorsClassic(); break;\n        }\n        return true;\n    }\n    return false;\n}\n\nvoid ImGui::ShowStyleEditor(ImGuiStyle* ref)\n{\n    IMGUI_DEMO_MARKER(\"Tools/Style Editor\");\n    // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to\n    // (without a reference style pointer, we will use one compared locally as a reference)\n    ImGuiStyle& style = ImGui::GetStyle();\n    static ImGuiStyle ref_saved_style;\n\n    // Default to using internal storage as reference\n    static bool init = true;\n    if (init && ref == NULL)\n        ref_saved_style = style;\n    init = false;\n    if (ref == NULL)\n        ref = &ref_saved_style;\n\n    ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f);\n\n    if (ImGui::ShowStyleSelector(\"Colors##Selector\"))\n        ref_saved_style = style;\n    ImGui::ShowFontSelector(\"Fonts##Selector\");\n\n    // Simplified Settings (expose floating-pointer border sizes as boolean representing 0.0f or 1.0f)\n    if (ImGui::SliderFloat(\"FrameRounding\", &style.FrameRounding, 0.0f, 12.0f, \"%.0f\"))\n        style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding\n    { bool border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox(\"WindowBorder\", &border)) { style.WindowBorderSize = border ? 1.0f : 0.0f; } }\n    ImGui::SameLine();\n    { bool border = (style.FrameBorderSize > 0.0f);  if (ImGui::Checkbox(\"FrameBorder\",  &border)) { style.FrameBorderSize  = border ? 1.0f : 0.0f; } }\n    ImGui::SameLine();\n    { bool border = (style.PopupBorderSize > 0.0f);  if (ImGui::Checkbox(\"PopupBorder\",  &border)) { style.PopupBorderSize  = border ? 1.0f : 0.0f; } }\n\n    // Save/Revert button\n    if (ImGui::Button(\"Save Ref\"))\n        *ref = ref_saved_style = style;\n    ImGui::SameLine();\n    if (ImGui::Button(\"Revert Ref\"))\n        style = *ref;\n    ImGui::SameLine();\n    HelpMarker(\n        \"Save/Revert in local non-persistent storage. Default Colors definition are not affected. \"\n        \"Use \\\"Export\\\" below to save them somewhere.\");\n\n    ImGui::Separator();\n\n    if (ImGui::BeginTabBar(\"##tabs\", ImGuiTabBarFlags_None))\n    {\n        if (ImGui::BeginTabItem(\"Sizes\"))\n        {\n            ImGui::SeparatorText(\"Main\");\n            ImGui::SliderFloat2(\"WindowPadding\", (float*)&style.WindowPadding, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"FramePadding\", (float*)&style.FramePadding, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"ItemSpacing\", (float*)&style.ItemSpacing, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"ItemInnerSpacing\", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"TouchExtraPadding\", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, \"%.0f\");\n            ImGui::SliderFloat(\"IndentSpacing\", &style.IndentSpacing, 0.0f, 30.0f, \"%.0f\");\n            ImGui::SliderFloat(\"ScrollbarSize\", &style.ScrollbarSize, 1.0f, 20.0f, \"%.0f\");\n            ImGui::SliderFloat(\"GrabMinSize\", &style.GrabMinSize, 1.0f, 20.0f, \"%.0f\");\n\n            ImGui::SeparatorText(\"Borders\");\n            ImGui::SliderFloat(\"WindowBorderSize\", &style.WindowBorderSize, 0.0f, 1.0f, \"%.0f\");\n            ImGui::SliderFloat(\"ChildBorderSize\", &style.ChildBorderSize, 0.0f, 1.0f, \"%.0f\");\n            ImGui::SliderFloat(\"PopupBorderSize\", &style.PopupBorderSize, 0.0f, 1.0f, \"%.0f\");\n            ImGui::SliderFloat(\"FrameBorderSize\", &style.FrameBorderSize, 0.0f, 1.0f, \"%.0f\");\n            ImGui::SliderFloat(\"TabBorderSize\", &style.TabBorderSize, 0.0f, 1.0f, \"%.0f\");\n            ImGui::SliderFloat(\"TabBarBorderSize\", &style.TabBarBorderSize, 0.0f, 2.0f, \"%.0f\");\n\n            ImGui::SeparatorText(\"Rounding\");\n            ImGui::SliderFloat(\"WindowRounding\", &style.WindowRounding, 0.0f, 12.0f, \"%.0f\");\n            ImGui::SliderFloat(\"ChildRounding\", &style.ChildRounding, 0.0f, 12.0f, \"%.0f\");\n            ImGui::SliderFloat(\"FrameRounding\", &style.FrameRounding, 0.0f, 12.0f, \"%.0f\");\n            ImGui::SliderFloat(\"PopupRounding\", &style.PopupRounding, 0.0f, 12.0f, \"%.0f\");\n            ImGui::SliderFloat(\"ScrollbarRounding\", &style.ScrollbarRounding, 0.0f, 12.0f, \"%.0f\");\n            ImGui::SliderFloat(\"GrabRounding\", &style.GrabRounding, 0.0f, 12.0f, \"%.0f\");\n            ImGui::SliderFloat(\"TabRounding\", &style.TabRounding, 0.0f, 12.0f, \"%.0f\");\n\n            ImGui::SeparatorText(\"Tables\");\n            ImGui::SliderFloat2(\"CellPadding\", (float*)&style.CellPadding, 0.0f, 20.0f, \"%.0f\");\n            ImGui::SliderAngle(\"TableAngledHeadersAngle\", &style.TableAngledHeadersAngle, -50.0f, +50.0f);\n\n            ImGui::SeparatorText(\"Widgets\");\n            ImGui::SliderFloat2(\"WindowTitleAlign\", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, \"%.2f\");\n            int window_menu_button_position = style.WindowMenuButtonPosition + 1;\n            if (ImGui::Combo(\"WindowMenuButtonPosition\", (int*)&window_menu_button_position, \"None\\0Left\\0Right\\0\"))\n                style.WindowMenuButtonPosition = window_menu_button_position - 1;\n            ImGui::Combo(\"ColorButtonPosition\", (int*)&style.ColorButtonPosition, \"Left\\0Right\\0\");\n            ImGui::SliderFloat2(\"ButtonTextAlign\", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, \"%.2f\");\n            ImGui::SameLine(); HelpMarker(\"Alignment applies when a button is larger than its text content.\");\n            ImGui::SliderFloat2(\"SelectableTextAlign\", (float*)&style.SelectableTextAlign, 0.0f, 1.0f, \"%.2f\");\n            ImGui::SameLine(); HelpMarker(\"Alignment applies when a selectable is larger than its text content.\");\n            ImGui::SliderFloat(\"SeparatorTextBorderSize\", &style.SeparatorTextBorderSize, 0.0f, 10.0f, \"%.0f\");\n            ImGui::SliderFloat2(\"SeparatorTextAlign\", (float*)&style.SeparatorTextAlign, 0.0f, 1.0f, \"%.2f\");\n            ImGui::SliderFloat2(\"SeparatorTextPadding\", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, \"%.0f\");\n            ImGui::SliderFloat(\"LogSliderDeadzone\", &style.LogSliderDeadzone, 0.0f, 12.0f, \"%.0f\");\n\n            ImGui::SeparatorText(\"Tooltips\");\n            for (int n = 0; n < 2; n++)\n                if (ImGui::TreeNodeEx(n == 0 ? \"HoverFlagsForTooltipMouse\" : \"HoverFlagsForTooltipNav\"))\n                {\n                    ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav;\n                    ImGui::CheckboxFlags(\"ImGuiHoveredFlags_DelayNone\", p, ImGuiHoveredFlags_DelayNone);\n                    ImGui::CheckboxFlags(\"ImGuiHoveredFlags_DelayShort\", p, ImGuiHoveredFlags_DelayShort);\n                    ImGui::CheckboxFlags(\"ImGuiHoveredFlags_DelayNormal\", p, ImGuiHoveredFlags_DelayNormal);\n                    ImGui::CheckboxFlags(\"ImGuiHoveredFlags_Stationary\", p, ImGuiHoveredFlags_Stationary);\n                    ImGui::CheckboxFlags(\"ImGuiHoveredFlags_NoSharedDelay\", p, ImGuiHoveredFlags_NoSharedDelay);\n                    ImGui::TreePop();\n                }\n\n            ImGui::SeparatorText(\"Misc\");\n            ImGui::SliderFloat2(\"DisplaySafeAreaPadding\", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, \"%.0f\"); ImGui::SameLine(); HelpMarker(\"Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured).\");\n\n            ImGui::EndTabItem();\n        }\n\n        if (ImGui::BeginTabItem(\"Colors\"))\n        {\n            static int output_dest = 0;\n            static bool output_only_modified = true;\n            if (ImGui::Button(\"Export\"))\n            {\n                if (output_dest == 0)\n                    ImGui::LogToClipboard();\n                else\n                    ImGui::LogToTTY();\n                ImGui::LogText(\"ImVec4* colors = ImGui::GetStyle().Colors;\" IM_NEWLINE);\n                for (int i = 0; i < ImGuiCol_COUNT; i++)\n                {\n                    const ImVec4& col = style.Colors[i];\n                    const char* name = ImGui::GetStyleColorName(i);\n                    if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0)\n                        ImGui::LogText(\"colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);\" IM_NEWLINE,\n                            name, 23 - (int)strlen(name), \"\", col.x, col.y, col.z, col.w);\n                }\n                ImGui::LogFinish();\n            }\n            ImGui::SameLine(); ImGui::SetNextItemWidth(120); ImGui::Combo(\"##output_type\", &output_dest, \"To Clipboard\\0To TTY\\0\");\n            ImGui::SameLine(); ImGui::Checkbox(\"Only Modified Colors\", &output_only_modified);\n\n            static ImGuiTextFilter filter;\n            filter.Draw(\"Filter colors\", ImGui::GetFontSize() * 16);\n\n            static ImGuiColorEditFlags alpha_flags = 0;\n            if (ImGui::RadioButton(\"Opaque\", alpha_flags == ImGuiColorEditFlags_None))             { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine();\n            if (ImGui::RadioButton(\"Alpha\",  alpha_flags == ImGuiColorEditFlags_AlphaPreview))     { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine();\n            if (ImGui::RadioButton(\"Both\",   alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine();\n            HelpMarker(\n                \"In the color list:\\n\"\n                \"Left-click on color square to open color picker,\\n\"\n                \"Right-click to open edit options menu.\");\n\n            ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, ImGui::GetTextLineHeightWithSpacing() * 10), ImVec2(FLT_MAX, FLT_MAX));\n            ImGui::BeginChild(\"##colors\", ImVec2(0, 0), ImGuiChildFlags_Border, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened);\n            ImGui::PushItemWidth(ImGui::GetFontSize() * -12);\n            for (int i = 0; i < ImGuiCol_COUNT; i++)\n            {\n                const char* name = ImGui::GetStyleColorName(i);\n                if (!filter.PassFilter(name))\n                    continue;\n                ImGui::PushID(i);\n                ImGui::ColorEdit4(\"##color\", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags);\n                if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0)\n                {\n                    // Tips: in a real user application, you may want to merge and use an icon font into the main font,\n                    // so instead of \"Save\"/\"Revert\" you'd use icons!\n                    // Read the FAQ and docs/FONTS.md about using icon fonts. It's really easy and super convenient!\n                    ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button(\"Save\")) { ref->Colors[i] = style.Colors[i]; }\n                    ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button(\"Revert\")) { style.Colors[i] = ref->Colors[i]; }\n                }\n                ImGui::SameLine(0.0f, style.ItemInnerSpacing.x);\n                ImGui::TextUnformatted(name);\n                ImGui::PopID();\n            }\n            ImGui::PopItemWidth();\n            ImGui::EndChild();\n\n            ImGui::EndTabItem();\n        }\n\n        if (ImGui::BeginTabItem(\"Fonts\"))\n        {\n            ImGuiIO& io = ImGui::GetIO();\n            ImFontAtlas* atlas = io.Fonts;\n            HelpMarker(\"Read FAQ and docs/FONTS.md for details on font loading.\");\n            ImGui::ShowFontAtlas(atlas);\n\n            // Post-baking font scaling. Note that this is NOT the nice way of scaling fonts, read below.\n            // (we enforce hard clamping manually as by default DragFloat/SliderFloat allows CTRL+Click text to get out of bounds).\n            const float MIN_SCALE = 0.3f;\n            const float MAX_SCALE = 2.0f;\n            HelpMarker(\n                \"Those are old settings provided for convenience.\\n\"\n                \"However, the _correct_ way of scaling your UI is currently to reload your font at the designed size, \"\n                \"rebuild the font atlas, and call style.ScaleAllSizes() on a reference ImGuiStyle structure.\\n\"\n                \"Using those settings here will give you poor quality results.\");\n            static float window_scale = 1.0f;\n            ImGui::PushItemWidth(ImGui::GetFontSize() * 8);\n            if (ImGui::DragFloat(\"window scale\", &window_scale, 0.005f, MIN_SCALE, MAX_SCALE, \"%.2f\", ImGuiSliderFlags_AlwaysClamp)) // Scale only this window\n                ImGui::SetWindowFontScale(window_scale);\n            ImGui::DragFloat(\"global scale\", &io.FontGlobalScale, 0.005f, MIN_SCALE, MAX_SCALE, \"%.2f\", ImGuiSliderFlags_AlwaysClamp); // Scale everything\n            ImGui::PopItemWidth();\n\n            ImGui::EndTabItem();\n        }\n\n        if (ImGui::BeginTabItem(\"Rendering\"))\n        {\n            ImGui::Checkbox(\"Anti-aliased lines\", &style.AntiAliasedLines);\n            ImGui::SameLine();\n            HelpMarker(\"When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well.\");\n\n            ImGui::Checkbox(\"Anti-aliased lines use texture\", &style.AntiAliasedLinesUseTex);\n            ImGui::SameLine();\n            HelpMarker(\"Faster lines using texture data. Require backend to render with bilinear filtering (not point/nearest filtering).\");\n\n            ImGui::Checkbox(\"Anti-aliased fill\", &style.AntiAliasedFill);\n            ImGui::PushItemWidth(ImGui::GetFontSize() * 8);\n            ImGui::DragFloat(\"Curve Tessellation Tolerance\", &style.CurveTessellationTol, 0.02f, 0.10f, 10.0f, \"%.2f\");\n            if (style.CurveTessellationTol < 0.10f) style.CurveTessellationTol = 0.10f;\n\n            // When editing the \"Circle Segment Max Error\" value, draw a preview of its effect on auto-tessellated circles.\n            ImGui::DragFloat(\"Circle Tessellation Max Error\", &style.CircleTessellationMaxError , 0.005f, 0.10f, 5.0f, \"%.2f\", ImGuiSliderFlags_AlwaysClamp);\n            const bool show_samples = ImGui::IsItemActive();\n            if (show_samples)\n                ImGui::SetNextWindowPos(ImGui::GetCursorScreenPos());\n            if (show_samples && ImGui::BeginTooltip())\n            {\n                ImGui::TextUnformatted(\"(R = radius, N = number of segments)\");\n                ImGui::Spacing();\n                ImDrawList* draw_list = ImGui::GetWindowDrawList();\n                const float min_widget_width = ImGui::CalcTextSize(\"N: MMM\\nR: MMM\").x;\n                for (int n = 0; n < 8; n++)\n                {\n                    const float RAD_MIN = 5.0f;\n                    const float RAD_MAX = 70.0f;\n                    const float rad = RAD_MIN + (RAD_MAX - RAD_MIN) * (float)n / (8.0f - 1.0f);\n\n                    ImGui::BeginGroup();\n\n                    ImGui::Text(\"R: %.f\\nN: %d\", rad, draw_list->_CalcCircleAutoSegmentCount(rad));\n\n                    const float canvas_width = IM_MAX(min_widget_width, rad * 2.0f);\n                    const float offset_x     = floorf(canvas_width * 0.5f);\n                    const float offset_y     = floorf(RAD_MAX);\n\n                    const ImVec2 p1 = ImGui::GetCursorScreenPos();\n                    draw_list->AddCircle(ImVec2(p1.x + offset_x, p1.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text));\n                    ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));\n\n                    /*\n                    const ImVec2 p2 = ImGui::GetCursorScreenPos();\n                    draw_list->AddCircleFilled(ImVec2(p2.x + offset_x, p2.y + offset_y), rad, ImGui::GetColorU32(ImGuiCol_Text));\n                    ImGui::Dummy(ImVec2(canvas_width, RAD_MAX * 2));\n                    */\n\n                    ImGui::EndGroup();\n                    ImGui::SameLine();\n                }\n                ImGui::EndTooltip();\n            }\n            ImGui::SameLine();\n            HelpMarker(\"When drawing circle primitives with \\\"num_segments == 0\\\" tesselation will be calculated automatically.\");\n\n            ImGui::DragFloat(\"Global Alpha\", &style.Alpha, 0.005f, 0.20f, 1.0f, \"%.2f\"); // Not exposing zero here so user doesn't \"lose\" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero.\n            ImGui::DragFloat(\"Disabled Alpha\", &style.DisabledAlpha, 0.005f, 0.0f, 1.0f, \"%.2f\"); ImGui::SameLine(); HelpMarker(\"Additional alpha multiplier for disabled items (multiply over current value of Alpha).\");\n            ImGui::PopItemWidth();\n\n            ImGui::EndTabItem();\n        }\n\n        ImGui::EndTabBar();\n    }\n\n    ImGui::PopItemWidth();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] User Guide / ShowUserGuide()\n//-----------------------------------------------------------------------------\n\nvoid ImGui::ShowUserGuide()\n{\n    ImGuiIO& io = ImGui::GetIO();\n    ImGui::BulletText(\"Double-click on title bar to collapse window.\");\n    ImGui::BulletText(\n        \"Click and drag on lower corner to resize window\\n\"\n        \"(double-click to auto fit window to its contents).\");\n    ImGui::BulletText(\"CTRL+Click on a slider or drag box to input value as text.\");\n    ImGui::BulletText(\"TAB/SHIFT+TAB to cycle through keyboard editable fields.\");\n    ImGui::BulletText(\"CTRL+Tab to select a window.\");\n    if (io.FontAllowUserScaling)\n        ImGui::BulletText(\"CTRL+Mouse Wheel to zoom window contents.\");\n    ImGui::BulletText(\"While inputing text:\\n\");\n    ImGui::Indent();\n    ImGui::BulletText(\"CTRL+Left/Right to word jump.\");\n    ImGui::BulletText(\"CTRL+A or double-click to select all.\");\n    ImGui::BulletText(\"CTRL+X/C/V to use clipboard cut/copy/paste.\");\n    ImGui::BulletText(\"CTRL+Z,CTRL+Y to undo/redo.\");\n    ImGui::BulletText(\"ESCAPE to revert.\");\n    ImGui::Unindent();\n    ImGui::BulletText(\"With keyboard navigation enabled:\");\n    ImGui::Indent();\n    ImGui::BulletText(\"Arrow keys to navigate.\");\n    ImGui::BulletText(\"Space to activate a widget.\");\n    ImGui::BulletText(\"Return to input text into a widget.\");\n    ImGui::BulletText(\"Escape to deactivate a widget, close popup, exit child window.\");\n    ImGui::BulletText(\"Alt to jump to the menu layer of a window.\");\n    ImGui::Unindent();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Main Menu Bar / ShowExampleAppMainMenuBar()\n//-----------------------------------------------------------------------------\n// - ShowExampleAppMainMenuBar()\n// - ShowExampleMenuFile()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a \"main\" fullscreen menu bar and populating it.\n// Note the difference between BeginMainMenuBar() and BeginMenuBar():\n// - BeginMenuBar() = menu-bar inside current window (which needs the ImGuiWindowFlags_MenuBar flag!)\n// - BeginMainMenuBar() = helper to create menu-bar-sized window at the top of the main viewport + call BeginMenuBar() into it.\nstatic void ShowExampleAppMainMenuBar()\n{\n    if (ImGui::BeginMainMenuBar())\n    {\n        if (ImGui::BeginMenu(\"File\"))\n        {\n            ShowExampleMenuFile();\n            ImGui::EndMenu();\n        }\n        if (ImGui::BeginMenu(\"Edit\"))\n        {\n            if (ImGui::MenuItem(\"Undo\", \"CTRL+Z\")) {}\n            if (ImGui::MenuItem(\"Redo\", \"CTRL+Y\", false, false)) {}  // Disabled item\n            ImGui::Separator();\n            if (ImGui::MenuItem(\"Cut\", \"CTRL+X\")) {}\n            if (ImGui::MenuItem(\"Copy\", \"CTRL+C\")) {}\n            if (ImGui::MenuItem(\"Paste\", \"CTRL+V\")) {}\n            ImGui::EndMenu();\n        }\n        ImGui::EndMainMenuBar();\n    }\n}\n\n// Note that shortcuts are currently provided for display only\n// (future version will add explicit flags to BeginMenu() to request processing shortcuts)\nstatic void ShowExampleMenuFile()\n{\n    IMGUI_DEMO_MARKER(\"Examples/Menu\");\n    ImGui::MenuItem(\"(demo menu)\", NULL, false, false);\n    if (ImGui::MenuItem(\"New\")) {}\n    if (ImGui::MenuItem(\"Open\", \"Ctrl+O\")) {}\n    if (ImGui::BeginMenu(\"Open Recent\"))\n    {\n        ImGui::MenuItem(\"fish_hat.c\");\n        ImGui::MenuItem(\"fish_hat.inl\");\n        ImGui::MenuItem(\"fish_hat.h\");\n        if (ImGui::BeginMenu(\"More..\"))\n        {\n            ImGui::MenuItem(\"Hello\");\n            ImGui::MenuItem(\"Sailor\");\n            if (ImGui::BeginMenu(\"Recurse..\"))\n            {\n                ShowExampleMenuFile();\n                ImGui::EndMenu();\n            }\n            ImGui::EndMenu();\n        }\n        ImGui::EndMenu();\n    }\n    if (ImGui::MenuItem(\"Save\", \"Ctrl+S\")) {}\n    if (ImGui::MenuItem(\"Save As..\")) {}\n\n    ImGui::Separator();\n    IMGUI_DEMO_MARKER(\"Examples/Menu/Options\");\n    if (ImGui::BeginMenu(\"Options\"))\n    {\n        static bool enabled = true;\n        ImGui::MenuItem(\"Enabled\", \"\", &enabled);\n        ImGui::BeginChild(\"child\", ImVec2(0, 60), ImGuiChildFlags_Border);\n        for (int i = 0; i < 10; i++)\n            ImGui::Text(\"Scrolling Text %d\", i);\n        ImGui::EndChild();\n        static float f = 0.5f;\n        static int n = 0;\n        ImGui::SliderFloat(\"Value\", &f, 0.0f, 1.0f);\n        ImGui::InputFloat(\"Input\", &f, 0.1f);\n        ImGui::Combo(\"Combo\", &n, \"Yes\\0No\\0Maybe\\0\\0\");\n        ImGui::EndMenu();\n    }\n\n    IMGUI_DEMO_MARKER(\"Examples/Menu/Colors\");\n    if (ImGui::BeginMenu(\"Colors\"))\n    {\n        float sz = ImGui::GetTextLineHeight();\n        for (int i = 0; i < ImGuiCol_COUNT; i++)\n        {\n            const char* name = ImGui::GetStyleColorName((ImGuiCol)i);\n            ImVec2 p = ImGui::GetCursorScreenPos();\n            ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + sz, p.y + sz), ImGui::GetColorU32((ImGuiCol)i));\n            ImGui::Dummy(ImVec2(sz, sz));\n            ImGui::SameLine();\n            ImGui::MenuItem(name);\n        }\n        ImGui::EndMenu();\n    }\n\n    // Here we demonstrate appending again to the \"Options\" menu (which we already created above)\n    // Of course in this demo it is a little bit silly that this function calls BeginMenu(\"Options\") twice.\n    // In a real code-base using it would make senses to use this feature from very different code locations.\n    if (ImGui::BeginMenu(\"Options\")) // <-- Append!\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Menu/Append to an existing menu\");\n        static bool b = true;\n        ImGui::Checkbox(\"SomeOption\", &b);\n        ImGui::EndMenu();\n    }\n\n    if (ImGui::BeginMenu(\"Disabled\", false)) // Disabled\n    {\n        IM_ASSERT(0);\n    }\n    if (ImGui::MenuItem(\"Checked\", NULL, true)) {}\n    ImGui::Separator();\n    if (ImGui::MenuItem(\"Quit\", \"Alt+F4\")) {}\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Debug Console / ShowExampleAppConsole()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a simple console window, with scrolling, filtering, completion and history.\n// For the console example, we are using a more C++ like approach of declaring a class to hold both data and functions.\nstruct ExampleAppConsole\n{\n    char                  InputBuf[256];\n    ImVector<char*>       Items;\n    ImVector<const char*> Commands;\n    ImVector<char*>       History;\n    int                   HistoryPos;    // -1: new line, 0..History.Size-1 browsing history.\n    ImGuiTextFilter       Filter;\n    bool                  AutoScroll;\n    bool                  ScrollToBottom;\n\n    ExampleAppConsole()\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Console\");\n        ClearLog();\n        memset(InputBuf, 0, sizeof(InputBuf));\n        HistoryPos = -1;\n\n        // \"CLASSIFY\" is here to provide the test case where \"C\"+[tab] completes to \"CL\" and display multiple matches.\n        Commands.push_back(\"HELP\");\n        Commands.push_back(\"HISTORY\");\n        Commands.push_back(\"CLEAR\");\n        Commands.push_back(\"CLASSIFY\");\n        AutoScroll = true;\n        ScrollToBottom = false;\n        AddLog(\"Welcome to Dear ImGui!\");\n    }\n    ~ExampleAppConsole()\n    {\n        ClearLog();\n        for (int i = 0; i < History.Size; i++)\n            free(History[i]);\n    }\n\n    // Portable helpers\n    static int   Stricmp(const char* s1, const char* s2)         { int d; while ((d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; } return d; }\n    static int   Strnicmp(const char* s1, const char* s2, int n) { int d = 0; while (n > 0 && (d = toupper(*s2) - toupper(*s1)) == 0 && *s1) { s1++; s2++; n--; } return d; }\n    static char* Strdup(const char* s)                           { IM_ASSERT(s); size_t len = strlen(s) + 1; void* buf = malloc(len); IM_ASSERT(buf); return (char*)memcpy(buf, (const void*)s, len); }\n    static void  Strtrim(char* s)                                { char* str_end = s + strlen(s); while (str_end > s && str_end[-1] == ' ') str_end--; *str_end = 0; }\n\n    void    ClearLog()\n    {\n        for (int i = 0; i < Items.Size; i++)\n            free(Items[i]);\n        Items.clear();\n    }\n\n    void    AddLog(const char* fmt, ...) IM_FMTARGS(2)\n    {\n        // FIXME-OPT\n        char buf[1024];\n        va_list args;\n        va_start(args, fmt);\n        vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args);\n        buf[IM_ARRAYSIZE(buf)-1] = 0;\n        va_end(args);\n        Items.push_back(Strdup(buf));\n    }\n\n    void    Draw(const char* title, bool* p_open)\n    {\n        ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);\n        if (!ImGui::Begin(title, p_open))\n        {\n            ImGui::End();\n            return;\n        }\n\n        // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar.\n        // So e.g. IsItemHovered() will return true when hovering the title bar.\n        // Here we create a context menu only available from the title bar.\n        if (ImGui::BeginPopupContextItem())\n        {\n            if (ImGui::MenuItem(\"Close Console\"))\n                *p_open = false;\n            ImGui::EndPopup();\n        }\n\n        ImGui::TextWrapped(\n            \"This example implements a console with basic coloring, completion (TAB key) and history (Up/Down keys). A more elaborate \"\n            \"implementation may want to store entries along with extra data such as timestamp, emitter, etc.\");\n        ImGui::TextWrapped(\"Enter 'HELP' for help.\");\n\n        // TODO: display items starting from the bottom\n\n        if (ImGui::SmallButton(\"Add Debug Text\"))  { AddLog(\"%d some text\", Items.Size); AddLog(\"some more text\"); AddLog(\"display very important message here!\"); }\n        ImGui::SameLine();\n        if (ImGui::SmallButton(\"Add Debug Error\")) { AddLog(\"[error] something went wrong\"); }\n        ImGui::SameLine();\n        if (ImGui::SmallButton(\"Clear\"))           { ClearLog(); }\n        ImGui::SameLine();\n        bool copy_to_clipboard = ImGui::SmallButton(\"Copy\");\n        //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog(\"Spam %f\", t); }\n\n        ImGui::Separator();\n\n        // Options menu\n        if (ImGui::BeginPopup(\"Options\"))\n        {\n            ImGui::Checkbox(\"Auto-scroll\", &AutoScroll);\n            ImGui::EndPopup();\n        }\n\n        // Options, Filter\n        if (ImGui::Button(\"Options\"))\n            ImGui::OpenPopup(\"Options\");\n        ImGui::SameLine();\n        Filter.Draw(\"Filter (\\\"incl,-excl\\\") (\\\"error\\\")\", 180);\n        ImGui::Separator();\n\n        // Reserve enough left-over height for 1 separator + 1 input text\n        const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();\n        if (ImGui::BeginChild(\"ScrollingRegion\", ImVec2(0, -footer_height_to_reserve), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar))\n        {\n            if (ImGui::BeginPopupContextWindow())\n            {\n                if (ImGui::Selectable(\"Clear\")) ClearLog();\n                ImGui::EndPopup();\n            }\n\n            // Display every line as a separate entry so we can change their color or add custom widgets.\n            // If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end());\n            // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping\n            // to only process visible items. The clipper will automatically measure the height of your first item and then\n            // \"seek\" to display only items in the visible area.\n            // To use the clipper we can replace your standard loop:\n            //      for (int i = 0; i < Items.Size; i++)\n            //   With:\n            //      ImGuiListClipper clipper;\n            //      clipper.Begin(Items.Size);\n            //      while (clipper.Step())\n            //         for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)\n            // - That your items are evenly spaced (same height)\n            // - That you have cheap random access to your elements (you can access them given their index,\n            //   without processing all the ones before)\n            // You cannot this code as-is if a filter is active because it breaks the 'cheap random-access' property.\n            // We would need random-access on the post-filtered list.\n            // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices\n            // or offsets of items that passed the filtering test, recomputing this array when user changes the filter,\n            // and appending newly elements as they are inserted. This is left as a task to the user until we can manage\n            // to improve this example code!\n            // If your items are of variable height:\n            // - Split them into same height items would be simpler and facilitate random-seeking into your list.\n            // - Consider using manual call to IsRectVisible() and skipping extraneous decoration from your items.\n            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1)); // Tighten spacing\n            if (copy_to_clipboard)\n                ImGui::LogToClipboard();\n            for (const char* item : Items)\n            {\n                if (!Filter.PassFilter(item))\n                    continue;\n\n                // Normally you would store more information in your item than just a string.\n                // (e.g. make Items[] an array of structure, store color/type etc.)\n                ImVec4 color;\n                bool has_color = false;\n                if (strstr(item, \"[error]\")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }\n                else if (strncmp(item, \"# \", 2) == 0) { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; }\n                if (has_color)\n                    ImGui::PushStyleColor(ImGuiCol_Text, color);\n                ImGui::TextUnformatted(item);\n                if (has_color)\n                    ImGui::PopStyleColor();\n            }\n            if (copy_to_clipboard)\n                ImGui::LogFinish();\n\n            // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.\n            // Using a scrollbar or mouse-wheel will take away from the bottom edge.\n            if (ScrollToBottom || (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()))\n                ImGui::SetScrollHereY(1.0f);\n            ScrollToBottom = false;\n\n            ImGui::PopStyleVar();\n        }\n        ImGui::EndChild();\n        ImGui::Separator();\n\n        // Command-line\n        bool reclaim_focus = false;\n        ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory;\n        if (ImGui::InputText(\"Input\", InputBuf, IM_ARRAYSIZE(InputBuf), input_text_flags, &TextEditCallbackStub, (void*)this))\n        {\n            char* s = InputBuf;\n            Strtrim(s);\n            if (s[0])\n                ExecCommand(s);\n            strcpy(s, \"\");\n            reclaim_focus = true;\n        }\n\n        // Auto-focus on window apparition\n        ImGui::SetItemDefaultFocus();\n        if (reclaim_focus)\n            ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget\n\n        ImGui::End();\n    }\n\n    void    ExecCommand(const char* command_line)\n    {\n        AddLog(\"# %s\\n\", command_line);\n\n        // Insert into history. First find match and delete it so it can be pushed to the back.\n        // This isn't trying to be smart or optimal.\n        HistoryPos = -1;\n        for (int i = History.Size - 1; i >= 0; i--)\n            if (Stricmp(History[i], command_line) == 0)\n            {\n                free(History[i]);\n                History.erase(History.begin() + i);\n                break;\n            }\n        History.push_back(Strdup(command_line));\n\n        // Process command\n        if (Stricmp(command_line, \"CLEAR\") == 0)\n        {\n            ClearLog();\n        }\n        else if (Stricmp(command_line, \"HELP\") == 0)\n        {\n            AddLog(\"Commands:\");\n            for (int i = 0; i < Commands.Size; i++)\n                AddLog(\"- %s\", Commands[i]);\n        }\n        else if (Stricmp(command_line, \"HISTORY\") == 0)\n        {\n            int first = History.Size - 10;\n            for (int i = first > 0 ? first : 0; i < History.Size; i++)\n                AddLog(\"%3d: %s\\n\", i, History[i]);\n        }\n        else\n        {\n            AddLog(\"Unknown command: '%s'\\n\", command_line);\n        }\n\n        // On command input, we scroll to bottom even if AutoScroll==false\n        ScrollToBottom = true;\n    }\n\n    // In C++11 you'd be better off using lambdas for this sort of forwarding callbacks\n    static int TextEditCallbackStub(ImGuiInputTextCallbackData* data)\n    {\n        ExampleAppConsole* console = (ExampleAppConsole*)data->UserData;\n        return console->TextEditCallback(data);\n    }\n\n    int     TextEditCallback(ImGuiInputTextCallbackData* data)\n    {\n        //AddLog(\"cursor: %d, selection: %d-%d\", data->CursorPos, data->SelectionStart, data->SelectionEnd);\n        switch (data->EventFlag)\n        {\n        case ImGuiInputTextFlags_CallbackCompletion:\n            {\n                // Example of TEXT COMPLETION\n\n                // Locate beginning of current word\n                const char* word_end = data->Buf + data->CursorPos;\n                const char* word_start = word_end;\n                while (word_start > data->Buf)\n                {\n                    const char c = word_start[-1];\n                    if (c == ' ' || c == '\\t' || c == ',' || c == ';')\n                        break;\n                    word_start--;\n                }\n\n                // Build a list of candidates\n                ImVector<const char*> candidates;\n                for (int i = 0; i < Commands.Size; i++)\n                    if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0)\n                        candidates.push_back(Commands[i]);\n\n                if (candidates.Size == 0)\n                {\n                    // No match\n                    AddLog(\"No match for \\\"%.*s\\\"!\\n\", (int)(word_end - word_start), word_start);\n                }\n                else if (candidates.Size == 1)\n                {\n                    // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.\n                    data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));\n                    data->InsertChars(data->CursorPos, candidates[0]);\n                    data->InsertChars(data->CursorPos, \" \");\n                }\n                else\n                {\n                    // Multiple matches. Complete as much as we can..\n                    // So inputing \"C\"+Tab will complete to \"CL\" then display \"CLEAR\" and \"CLASSIFY\" as matches.\n                    int match_len = (int)(word_end - word_start);\n                    for (;;)\n                    {\n                        int c = 0;\n                        bool all_candidates_matches = true;\n                        for (int i = 0; i < candidates.Size && all_candidates_matches; i++)\n                            if (i == 0)\n                                c = toupper(candidates[i][match_len]);\n                            else if (c == 0 || c != toupper(candidates[i][match_len]))\n                                all_candidates_matches = false;\n                        if (!all_candidates_matches)\n                            break;\n                        match_len++;\n                    }\n\n                    if (match_len > 0)\n                    {\n                        data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));\n                        data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);\n                    }\n\n                    // List matches\n                    AddLog(\"Possible matches:\\n\");\n                    for (int i = 0; i < candidates.Size; i++)\n                        AddLog(\"- %s\\n\", candidates[i]);\n                }\n\n                break;\n            }\n        case ImGuiInputTextFlags_CallbackHistory:\n            {\n                // Example of HISTORY\n                const int prev_history_pos = HistoryPos;\n                if (data->EventKey == ImGuiKey_UpArrow)\n                {\n                    if (HistoryPos == -1)\n                        HistoryPos = History.Size - 1;\n                    else if (HistoryPos > 0)\n                        HistoryPos--;\n                }\n                else if (data->EventKey == ImGuiKey_DownArrow)\n                {\n                    if (HistoryPos != -1)\n                        if (++HistoryPos >= History.Size)\n                            HistoryPos = -1;\n                }\n\n                // A better implementation would preserve the data on the current input line along with cursor position.\n                if (prev_history_pos != HistoryPos)\n                {\n                    const char* history_str = (HistoryPos >= 0) ? History[HistoryPos] : \"\";\n                    data->DeleteChars(0, data->BufTextLen);\n                    data->InsertChars(0, history_str);\n                }\n            }\n        }\n        return 0;\n    }\n};\n\nstatic void ShowExampleAppConsole(bool* p_open)\n{\n    static ExampleAppConsole console;\n    console.Draw(\"Example: Console\", p_open);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Debug Log / ShowExampleAppLog()\n//-----------------------------------------------------------------------------\n\n// Usage:\n//  static ExampleAppLog my_log;\n//  my_log.AddLog(\"Hello %d world\\n\", 123);\n//  my_log.Draw(\"title\");\nstruct ExampleAppLog\n{\n    ImGuiTextBuffer     Buf;\n    ImGuiTextFilter     Filter;\n    ImVector<int>       LineOffsets; // Index to lines offset. We maintain this with AddLog() calls.\n    bool                AutoScroll;  // Keep scrolling if already at the bottom.\n\n    ExampleAppLog()\n    {\n        AutoScroll = true;\n        Clear();\n    }\n\n    void    Clear()\n    {\n        Buf.clear();\n        LineOffsets.clear();\n        LineOffsets.push_back(0);\n    }\n\n    void    AddLog(const char* fmt, ...) IM_FMTARGS(2)\n    {\n        int old_size = Buf.size();\n        va_list args;\n        va_start(args, fmt);\n        Buf.appendfv(fmt, args);\n        va_end(args);\n        for (int new_size = Buf.size(); old_size < new_size; old_size++)\n            if (Buf[old_size] == '\\n')\n                LineOffsets.push_back(old_size + 1);\n    }\n\n    void    Draw(const char* title, bool* p_open = NULL)\n    {\n        if (!ImGui::Begin(title, p_open))\n        {\n            ImGui::End();\n            return;\n        }\n\n        // Options menu\n        if (ImGui::BeginPopup(\"Options\"))\n        {\n            ImGui::Checkbox(\"Auto-scroll\", &AutoScroll);\n            ImGui::EndPopup();\n        }\n\n        // Main window\n        if (ImGui::Button(\"Options\"))\n            ImGui::OpenPopup(\"Options\");\n        ImGui::SameLine();\n        bool clear = ImGui::Button(\"Clear\");\n        ImGui::SameLine();\n        bool copy = ImGui::Button(\"Copy\");\n        ImGui::SameLine();\n        Filter.Draw(\"Filter\", -100.0f);\n\n        ImGui::Separator();\n\n        if (ImGui::BeginChild(\"scrolling\", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_HorizontalScrollbar))\n        {\n            if (clear)\n                Clear();\n            if (copy)\n                ImGui::LogToClipboard();\n\n            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));\n            const char* buf = Buf.begin();\n            const char* buf_end = Buf.end();\n            if (Filter.IsActive())\n            {\n                // In this example we don't use the clipper when Filter is enabled.\n                // This is because we don't have random access to the result of our filter.\n                // A real application processing logs with ten of thousands of entries may want to store the result of\n                // search/filter.. especially if the filtering function is not trivial (e.g. reg-exp).\n                for (int line_no = 0; line_no < LineOffsets.Size; line_no++)\n                {\n                    const char* line_start = buf + LineOffsets[line_no];\n                    const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;\n                    if (Filter.PassFilter(line_start, line_end))\n                        ImGui::TextUnformatted(line_start, line_end);\n                }\n            }\n            else\n            {\n                // The simplest and easy way to display the entire buffer:\n                //   ImGui::TextUnformatted(buf_begin, buf_end);\n                // And it'll just work. TextUnformatted() has specialization for large blob of text and will fast-forward\n                // to skip non-visible lines. Here we instead demonstrate using the clipper to only process lines that are\n                // within the visible area.\n                // If you have tens of thousands of items and their processing cost is non-negligible, coarse clipping them\n                // on your side is recommended. Using ImGuiListClipper requires\n                // - A) random access into your data\n                // - B) items all being the  same height,\n                // both of which we can handle since we have an array pointing to the beginning of each line of text.\n                // When using the filter (in the block of code above) we don't have random access into the data to display\n                // anymore, which is why we don't use the clipper. Storing or skimming through the search result would make\n                // it possible (and would be recommended if you want to search through tens of thousands of entries).\n                ImGuiListClipper clipper;\n                clipper.Begin(LineOffsets.Size);\n                while (clipper.Step())\n                {\n                    for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++)\n                    {\n                        const char* line_start = buf + LineOffsets[line_no];\n                        const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end;\n                        ImGui::TextUnformatted(line_start, line_end);\n                    }\n                }\n                clipper.End();\n            }\n            ImGui::PopStyleVar();\n\n            // Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.\n            // Using a scrollbar or mouse-wheel will take away from the bottom edge.\n            if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())\n                ImGui::SetScrollHereY(1.0f);\n        }\n        ImGui::EndChild();\n        ImGui::End();\n    }\n};\n\n// Demonstrate creating a simple log window with basic filtering.\nstatic void ShowExampleAppLog(bool* p_open)\n{\n    static ExampleAppLog log;\n\n    // For the demo: add a debug button _BEFORE_ the normal log window contents\n    // We take advantage of a rarely used feature: multiple calls to Begin()/End() are appending to the _same_ window.\n    // Most of the contents of the window will be added by the log.Draw() call.\n    ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver);\n    ImGui::Begin(\"Example: Log\", p_open);\n    IMGUI_DEMO_MARKER(\"Examples/Log\");\n    if (ImGui::SmallButton(\"[Debug] Add 5 entries\"))\n    {\n        static int counter = 0;\n        const char* categories[3] = { \"info\", \"warn\", \"error\" };\n        const char* words[] = { \"Bumfuzzled\", \"Cattywampus\", \"Snickersnee\", \"Abibliophobia\", \"Absquatulate\", \"Nincompoop\", \"Pauciloquent\" };\n        for (int n = 0; n < 5; n++)\n        {\n            const char* category = categories[counter % IM_ARRAYSIZE(categories)];\n            const char* word = words[counter % IM_ARRAYSIZE(words)];\n            log.AddLog(\"[%05d] [%s] Hello, current time is %.1f, here's a word: '%s'\\n\",\n                ImGui::GetFrameCount(), category, ImGui::GetTime(), word);\n            counter++;\n        }\n    }\n    ImGui::End();\n\n    // Actually call in the regular Log helper (which will Begin() into the same window as we just did)\n    log.Draw(\"Example: Log\", p_open);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Simple Layout / ShowExampleAppLayout()\n//-----------------------------------------------------------------------------\n\n// Demonstrate create a window with multiple child windows.\nstatic void ShowExampleAppLayout(bool* p_open)\n{\n    ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver);\n    if (ImGui::Begin(\"Example: Simple layout\", p_open, ImGuiWindowFlags_MenuBar))\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Simple layout\");\n        if (ImGui::BeginMenuBar())\n        {\n            if (ImGui::BeginMenu(\"File\"))\n            {\n                if (ImGui::MenuItem(\"Close\", \"Ctrl+W\")) { *p_open = false; }\n                ImGui::EndMenu();\n            }\n            ImGui::EndMenuBar();\n        }\n\n        // Left\n        static int selected = 0;\n        {\n            ImGui::BeginChild(\"left pane\", ImVec2(150, 0), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX);\n            for (int i = 0; i < 100; i++)\n            {\n                // FIXME: Good candidate to use ImGuiSelectableFlags_SelectOnNav\n                char label[128];\n                sprintf(label, \"MyObject %d\", i);\n                if (ImGui::Selectable(label, selected == i))\n                    selected = i;\n            }\n            ImGui::EndChild();\n        }\n        ImGui::SameLine();\n\n        // Right\n        {\n            ImGui::BeginGroup();\n            ImGui::BeginChild(\"item view\", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us\n            ImGui::Text(\"MyObject: %d\", selected);\n            ImGui::Separator();\n            if (ImGui::BeginTabBar(\"##Tabs\", ImGuiTabBarFlags_None))\n            {\n                if (ImGui::BeginTabItem(\"Description\"))\n                {\n                    ImGui::TextWrapped(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \");\n                    ImGui::EndTabItem();\n                }\n                if (ImGui::BeginTabItem(\"Details\"))\n                {\n                    ImGui::Text(\"ID: 0123456789\");\n                    ImGui::EndTabItem();\n                }\n                ImGui::EndTabBar();\n            }\n            ImGui::EndChild();\n            if (ImGui::Button(\"Revert\")) {}\n            ImGui::SameLine();\n            if (ImGui::Button(\"Save\")) {}\n            ImGui::EndGroup();\n        }\n    }\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Property Editor / ShowExampleAppPropertyEditor()\n//-----------------------------------------------------------------------------\n\nstatic void ShowPlaceholderObject(const char* prefix, int uid)\n{\n    // Use object uid as identifier. Most commonly you could also use the object pointer as a base ID.\n    ImGui::PushID(uid);\n\n    // Text and Tree nodes are less high than framed widgets, using AlignTextToFramePadding() we add vertical spacing to make the tree lines equal high.\n    ImGui::TableNextRow();\n    ImGui::TableSetColumnIndex(0);\n    ImGui::AlignTextToFramePadding();\n    bool node_open = ImGui::TreeNode(\"Object\", \"%s_%u\", prefix, uid);\n    ImGui::TableSetColumnIndex(1);\n    ImGui::Text(\"my sailor is rich\");\n\n    if (node_open)\n    {\n        static float placeholder_members[8] = { 0.0f, 0.0f, 1.0f, 3.1416f, 100.0f, 999.0f };\n        for (int i = 0; i < 8; i++)\n        {\n            ImGui::PushID(i); // Use field index as identifier.\n            if (i < 2)\n            {\n                ShowPlaceholderObject(\"Child\", 424242);\n            }\n            else\n            {\n                // Here we use a TreeNode to highlight on hover (we could use e.g. Selectable as well)\n                ImGui::TableNextRow();\n                ImGui::TableSetColumnIndex(0);\n                ImGui::AlignTextToFramePadding();\n                ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Bullet;\n                ImGui::TreeNodeEx(\"Field\", flags, \"Field_%d\", i);\n\n                ImGui::TableSetColumnIndex(1);\n                ImGui::SetNextItemWidth(-FLT_MIN);\n                if (i >= 5)\n                    ImGui::InputFloat(\"##value\", &placeholder_members[i], 1.0f);\n                else\n                    ImGui::DragFloat(\"##value\", &placeholder_members[i], 0.01f);\n                ImGui::NextColumn();\n            }\n            ImGui::PopID();\n        }\n        ImGui::TreePop();\n    }\n    ImGui::PopID();\n}\n\n// Demonstrate create a simple property editor.\n// This demo is a bit lackluster nowadays, would be nice to improve.\nstatic void ShowExampleAppPropertyEditor(bool* p_open)\n{\n    ImGui::SetNextWindowSize(ImVec2(430, 450), ImGuiCond_FirstUseEver);\n    if (!ImGui::Begin(\"Example: Property editor\", p_open))\n    {\n        ImGui::End();\n        return;\n    }\n\n    IMGUI_DEMO_MARKER(\"Examples/Property Editor\");\n    HelpMarker(\n        \"This example shows how you may implement a property editor using two columns.\\n\"\n        \"All objects/fields data are dummies here.\\n\");\n\n    ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));\n    if (ImGui::BeginTable(\"##split\", 2, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY))\n    {\n        ImGui::TableSetupScrollFreeze(0, 1);\n        ImGui::TableSetupColumn(\"Object\");\n        ImGui::TableSetupColumn(\"Contents\");\n        ImGui::TableHeadersRow();\n\n        // Iterate placeholder objects (all the same data)\n        for (int obj_i = 0; obj_i < 4; obj_i++)\n            ShowPlaceholderObject(\"Object\", obj_i);\n\n        ImGui::EndTable();\n    }\n    ImGui::PopStyleVar();\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Long Text / ShowExampleAppLongText()\n//-----------------------------------------------------------------------------\n\n// Demonstrate/test rendering huge amount of text, and the incidence of clipping.\nstatic void ShowExampleAppLongText(bool* p_open)\n{\n    ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver);\n    if (!ImGui::Begin(\"Example: Long text display\", p_open))\n    {\n        ImGui::End();\n        return;\n    }\n    IMGUI_DEMO_MARKER(\"Examples/Long text display\");\n\n    static int test_type = 0;\n    static ImGuiTextBuffer log;\n    static int lines = 0;\n    ImGui::Text(\"Printing unusually long amount of text.\");\n    ImGui::Combo(\"Test type\", &test_type,\n        \"Single call to TextUnformatted()\\0\"\n        \"Multiple calls to Text(), clipped\\0\"\n        \"Multiple calls to Text(), not clipped (slow)\\0\");\n    ImGui::Text(\"Buffer contents: %d lines, %d bytes\", lines, log.size());\n    if (ImGui::Button(\"Clear\")) { log.clear(); lines = 0; }\n    ImGui::SameLine();\n    if (ImGui::Button(\"Add 1000 lines\"))\n    {\n        for (int i = 0; i < 1000; i++)\n            log.appendf(\"%i The quick brown fox jumps over the lazy dog\\n\", lines + i);\n        lines += 1000;\n    }\n    ImGui::BeginChild(\"Log\");\n    switch (test_type)\n    {\n    case 0:\n        // Single call to TextUnformatted() with a big buffer\n        ImGui::TextUnformatted(log.begin(), log.end());\n        break;\n    case 1:\n        {\n            // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.\n            ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));\n            ImGuiListClipper clipper;\n            clipper.Begin(lines);\n            while (clipper.Step())\n                for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)\n                    ImGui::Text(\"%i The quick brown fox jumps over the lazy dog\", i);\n            ImGui::PopStyleVar();\n            break;\n        }\n    case 2:\n        // Multiple calls to Text(), not clipped (slow)\n        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));\n        for (int i = 0; i < lines; i++)\n            ImGui::Text(\"%i The quick brown fox jumps over the lazy dog\", i);\n        ImGui::PopStyleVar();\n        break;\n    }\n    ImGui::EndChild();\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Auto Resize / ShowExampleAppAutoResize()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a window which gets auto-resized according to its content.\nstatic void ShowExampleAppAutoResize(bool* p_open)\n{\n    if (!ImGui::Begin(\"Example: Auto-resizing window\", p_open, ImGuiWindowFlags_AlwaysAutoResize))\n    {\n        ImGui::End();\n        return;\n    }\n    IMGUI_DEMO_MARKER(\"Examples/Auto-resizing window\");\n\n    static int lines = 10;\n    ImGui::TextUnformatted(\n        \"Window will resize every-frame to the size of its content.\\n\"\n        \"Note that you probably don't want to query the window size to\\n\"\n        \"output your content because that would create a feedback loop.\");\n    ImGui::SliderInt(\"Number of lines\", &lines, 1, 20);\n    for (int i = 0; i < lines; i++)\n        ImGui::Text(\"%*sThis is line %d\", i * 4, \"\", i); // Pad with space to extend size horizontally\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Constrained Resize / ShowExampleAppConstrainedResize()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a window with custom resize constraints.\n// Note that size constraints currently don't work on a docked window (when in 'docking' branch)\nstatic void ShowExampleAppConstrainedResize(bool* p_open)\n{\n    struct CustomConstraints\n    {\n        // Helper functions to demonstrate programmatic constraints\n        // FIXME: This doesn't take account of decoration size (e.g. title bar), library should make this easier.\n        // FIXME: None of the three demos works consistently when resizing from borders.\n        static void AspectRatio(ImGuiSizeCallbackData* data)\n        {\n            float aspect_ratio = *(float*)data->UserData;\n            data->DesiredSize.y = (float)(int)(data->DesiredSize.x / aspect_ratio);\n        }\n        static void Square(ImGuiSizeCallbackData* data)\n        {\n            data->DesiredSize.x = data->DesiredSize.y = IM_MAX(data->DesiredSize.x, data->DesiredSize.y);\n        }\n        static void Step(ImGuiSizeCallbackData* data)\n        {\n            float step = *(float*)data->UserData;\n            data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step);\n        }\n    };\n\n    const char* test_desc[] =\n    {\n        \"Between 100x100 and 500x500\",\n        \"At least 100x100\",\n        \"Resize vertical + lock current width\",\n        \"Resize horizontal + lock current height\",\n        \"Width Between 400 and 500\",\n        \"Height at least 400\",\n        \"Custom: Aspect Ratio 16:9\",\n        \"Custom: Always Square\",\n        \"Custom: Fixed Steps (100)\",\n    };\n\n    // Options\n    static bool auto_resize = false;\n    static bool window_padding = true;\n    static int type = 6; // Aspect Ratio\n    static int display_lines = 10;\n\n    // Submit constraint\n    float aspect_ratio = 16.0f / 9.0f;\n    float fixed_step = 100.0f;\n    if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(500, 500));         // Between 100x100 and 500x500\n    if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100\n    if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0),    ImVec2(-1, FLT_MAX));      // Resize vertical + lock current width\n    if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1),    ImVec2(FLT_MAX, -1));      // Resize horizontal + lock current height\n    if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1),  ImVec2(500, -1));          // Width Between and 400 and 500\n    if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 500),  ImVec2(-1, FLT_MAX));      // Height at least 400\n    if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0),     ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::AspectRatio, (void*)&aspect_ratio);   // Aspect ratio\n    if (type == 7) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0),     ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square);                              // Always Square\n    if (type == 8) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0),     ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)&fixed_step);            // Fixed Step\n\n    // Submit window\n    if (!window_padding)\n        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));\n    const ImGuiWindowFlags window_flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0;\n    const bool window_open = ImGui::Begin(\"Example: Constrained Resize\", p_open, window_flags);\n    if (!window_padding)\n        ImGui::PopStyleVar();\n    if (window_open)\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Constrained Resizing window\");\n        if (ImGui::GetIO().KeyShift)\n        {\n            // Display a dummy viewport (in your real app you would likely use ImageButton() to display a texture.\n            ImVec2 avail_size = ImGui::GetContentRegionAvail();\n            ImVec2 pos = ImGui::GetCursorScreenPos();\n            ImGui::ColorButton(\"viewport\", ImVec4(0.5f, 0.2f, 0.5f, 1.0f), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop, avail_size);\n            ImGui::SetCursorScreenPos(ImVec2(pos.x + 10, pos.y + 10));\n            ImGui::Text(\"%.2f x %.2f\", avail_size.x, avail_size.y);\n        }\n        else\n        {\n            ImGui::Text(\"(Hold SHIFT to display a dummy viewport)\");\n            if (ImGui::Button(\"Set 200x200\")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine();\n            if (ImGui::Button(\"Set 500x500\")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine();\n            if (ImGui::Button(\"Set 800x200\")) { ImGui::SetWindowSize(ImVec2(800, 200)); }\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);\n            ImGui::Combo(\"Constraint\", &type, test_desc, IM_ARRAYSIZE(test_desc));\n            ImGui::SetNextItemWidth(ImGui::GetFontSize() * 20);\n            ImGui::DragInt(\"Lines\", &display_lines, 0.2f, 1, 100);\n            ImGui::Checkbox(\"Auto-resize\", &auto_resize);\n            ImGui::Checkbox(\"Window padding\", &window_padding);\n            for (int i = 0; i < display_lines; i++)\n                ImGui::Text(\"%*sHello, sailor! Making this line long enough for the example.\", i * 4, \"\");\n        }\n    }\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Simple overlay / ShowExampleAppSimpleOverlay()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a simple static window with no decoration\n// + a context-menu to choose which corner of the screen to use.\nstatic void ShowExampleAppSimpleOverlay(bool* p_open)\n{\n    static int location = 0;\n    ImGuiIO& io = ImGui::GetIO();\n    ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;\n    if (location >= 0)\n    {\n        const float PAD = 10.0f;\n        const ImGuiViewport* viewport = ImGui::GetMainViewport();\n        ImVec2 work_pos = viewport->WorkPos; // Use work area to avoid menu-bar/task-bar, if any!\n        ImVec2 work_size = viewport->WorkSize;\n        ImVec2 window_pos, window_pos_pivot;\n        window_pos.x = (location & 1) ? (work_pos.x + work_size.x - PAD) : (work_pos.x + PAD);\n        window_pos.y = (location & 2) ? (work_pos.y + work_size.y - PAD) : (work_pos.y + PAD);\n        window_pos_pivot.x = (location & 1) ? 1.0f : 0.0f;\n        window_pos_pivot.y = (location & 2) ? 1.0f : 0.0f;\n        ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot);\n        window_flags |= ImGuiWindowFlags_NoMove;\n    }\n    else if (location == -2)\n    {\n        // Center window\n        ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f));\n        window_flags |= ImGuiWindowFlags_NoMove;\n    }\n    ImGui::SetNextWindowBgAlpha(0.35f); // Transparent background\n    if (ImGui::Begin(\"Example: Simple overlay\", p_open, window_flags))\n    {\n        IMGUI_DEMO_MARKER(\"Examples/Simple Overlay\");\n        ImGui::Text(\"Simple overlay\\n\" \"(right-click to change position)\");\n        ImGui::Separator();\n        if (ImGui::IsMousePosValid())\n            ImGui::Text(\"Mouse Position: (%.1f,%.1f)\", io.MousePos.x, io.MousePos.y);\n        else\n            ImGui::Text(\"Mouse Position: <invalid>\");\n        if (ImGui::BeginPopupContextWindow())\n        {\n            if (ImGui::MenuItem(\"Custom\",       NULL, location == -1)) location = -1;\n            if (ImGui::MenuItem(\"Center\",       NULL, location == -2)) location = -2;\n            if (ImGui::MenuItem(\"Top-left\",     NULL, location == 0)) location = 0;\n            if (ImGui::MenuItem(\"Top-right\",    NULL, location == 1)) location = 1;\n            if (ImGui::MenuItem(\"Bottom-left\",  NULL, location == 2)) location = 2;\n            if (ImGui::MenuItem(\"Bottom-right\", NULL, location == 3)) location = 3;\n            if (p_open && ImGui::MenuItem(\"Close\")) *p_open = false;\n            ImGui::EndPopup();\n        }\n    }\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen()\n//-----------------------------------------------------------------------------\n\n// Demonstrate creating a window covering the entire screen/viewport\nstatic void ShowExampleAppFullscreen(bool* p_open)\n{\n    static bool use_work_area = true;\n    static ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings;\n\n    // We demonstrate using the full viewport area or the work area (without menu-bars, task-bars etc.)\n    // Based on your use case you may want one or the other.\n    const ImGuiViewport* viewport = ImGui::GetMainViewport();\n    ImGui::SetNextWindowPos(use_work_area ? viewport->WorkPos : viewport->Pos);\n    ImGui::SetNextWindowSize(use_work_area ? viewport->WorkSize : viewport->Size);\n\n    if (ImGui::Begin(\"Example: Fullscreen window\", p_open, flags))\n    {\n        ImGui::Checkbox(\"Use work area instead of main area\", &use_work_area);\n        ImGui::SameLine();\n        HelpMarker(\"Main Area = entire viewport,\\nWork Area = entire viewport minus sections used by the main menu bars, task bars etc.\\n\\nEnable the main-menu bar in Examples menu to see the difference.\");\n\n        ImGui::CheckboxFlags(\"ImGuiWindowFlags_NoBackground\", &flags, ImGuiWindowFlags_NoBackground);\n        ImGui::CheckboxFlags(\"ImGuiWindowFlags_NoDecoration\", &flags, ImGuiWindowFlags_NoDecoration);\n        ImGui::Indent();\n        ImGui::CheckboxFlags(\"ImGuiWindowFlags_NoTitleBar\", &flags, ImGuiWindowFlags_NoTitleBar);\n        ImGui::CheckboxFlags(\"ImGuiWindowFlags_NoCollapse\", &flags, ImGuiWindowFlags_NoCollapse);\n        ImGui::CheckboxFlags(\"ImGuiWindowFlags_NoScrollbar\", &flags, ImGuiWindowFlags_NoScrollbar);\n        ImGui::Unindent();\n\n        if (p_open && ImGui::Button(\"Close this window\"))\n            *p_open = false;\n    }\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Manipulating Window Titles / ShowExampleAppWindowTitles()\n//-----------------------------------------------------------------------------\n\n// Demonstrate the use of \"##\" and \"###\" in identifiers to manipulate ID generation.\n// This applies to all regular items as well.\n// Read FAQ section \"How can I have multiple widgets with the same label?\" for details.\nstatic void ShowExampleAppWindowTitles(bool*)\n{\n    const ImGuiViewport* viewport = ImGui::GetMainViewport();\n    const ImVec2 base_pos = viewport->Pos;\n\n    // By default, Windows are uniquely identified by their title.\n    // You can use the \"##\" and \"###\" markers to manipulate the display/ID.\n\n    // Using \"##\" to display same title but have unique identifier.\n    ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 100), ImGuiCond_FirstUseEver);\n    ImGui::Begin(\"Same title as another window##1\");\n    IMGUI_DEMO_MARKER(\"Examples/Manipulating window titles\");\n    ImGui::Text(\"This is window 1.\\nMy title is the same as window 2, but my identifier is unique.\");\n    ImGui::End();\n\n    ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 200), ImGuiCond_FirstUseEver);\n    ImGui::Begin(\"Same title as another window##2\");\n    ImGui::Text(\"This is window 2.\\nMy title is the same as window 1, but my identifier is unique.\");\n    ImGui::End();\n\n    // Using \"###\" to display a changing title but keep a static identifier \"AnimatedTitle\"\n    char buf[128];\n    sprintf(buf, \"Animated title %c %d###AnimatedTitle\", \"|/-\\\\\"[(int)(ImGui::GetTime() / 0.25f) & 3], ImGui::GetFrameCount());\n    ImGui::SetNextWindowPos(ImVec2(base_pos.x + 100, base_pos.y + 300), ImGuiCond_FirstUseEver);\n    ImGui::Begin(buf);\n    ImGui::Text(\"This window has a changing title.\");\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering()\n//-----------------------------------------------------------------------------\n\n// Demonstrate using the low-level ImDrawList to draw custom shapes.\nstatic void ShowExampleAppCustomRendering(bool* p_open)\n{\n    if (!ImGui::Begin(\"Example: Custom rendering\", p_open))\n    {\n        ImGui::End();\n        return;\n    }\n    IMGUI_DEMO_MARKER(\"Examples/Custom Rendering\");\n\n    // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of\n    // overloaded operators, etc. Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your\n    // types and ImVec2/ImVec4. Dear ImGui defines overloaded operators but they are internal to imgui.cpp and not\n    // exposed outside (to avoid messing with your types) In this example we are not using the maths operators!\n\n    if (ImGui::BeginTabBar(\"##TabBar\"))\n    {\n        if (ImGui::BeginTabItem(\"Primitives\"))\n        {\n            ImGui::PushItemWidth(-ImGui::GetFontSize() * 15);\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n\n            // Draw gradients\n            // (note that those are currently exacerbating our sRGB/Linear issues)\n            // Calling ImGui::GetColorU32() multiplies the given colors by the current Style Alpha, but you may pass the IM_COL32() directly as well..\n            ImGui::Text(\"Gradients\");\n            ImVec2 gradient_size = ImVec2(ImGui::CalcItemWidth(), ImGui::GetFrameHeight());\n            {\n                ImVec2 p0 = ImGui::GetCursorScreenPos();\n                ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);\n                ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255));\n                ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255));\n                draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);\n                ImGui::InvisibleButton(\"##gradient1\", gradient_size);\n            }\n            {\n                ImVec2 p0 = ImGui::GetCursorScreenPos();\n                ImVec2 p1 = ImVec2(p0.x + gradient_size.x, p0.y + gradient_size.y);\n                ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 255, 0, 255));\n                ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 0, 0, 255));\n                draw_list->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a);\n                ImGui::InvisibleButton(\"##gradient2\", gradient_size);\n            }\n\n            // Draw a bunch of primitives\n            ImGui::Text(\"All primitives\");\n            static float sz = 36.0f;\n            static float thickness = 3.0f;\n            static int ngon_sides = 6;\n            static bool circle_segments_override = false;\n            static int circle_segments_override_v = 12;\n            static bool curve_segments_override = false;\n            static int curve_segments_override_v = 8;\n            static ImVec4 colf = ImVec4(1.0f, 1.0f, 0.4f, 1.0f);\n            ImGui::DragFloat(\"Size\", &sz, 0.2f, 2.0f, 100.0f, \"%.0f\");\n            ImGui::DragFloat(\"Thickness\", &thickness, 0.05f, 1.0f, 8.0f, \"%.02f\");\n            ImGui::SliderInt(\"N-gon sides\", &ngon_sides, 3, 12);\n            ImGui::Checkbox(\"##circlesegmentoverride\", &circle_segments_override);\n            ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);\n            circle_segments_override |= ImGui::SliderInt(\"Circle segments override\", &circle_segments_override_v, 3, 40);\n            ImGui::Checkbox(\"##curvessegmentoverride\", &curve_segments_override);\n            ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);\n            curve_segments_override |= ImGui::SliderInt(\"Curves segments override\", &curve_segments_override_v, 3, 40);\n            ImGui::ColorEdit4(\"Color\", &colf.x);\n\n            const ImVec2 p = ImGui::GetCursorScreenPos();\n            const ImU32 col = ImColor(colf);\n            const float spacing = 10.0f;\n            const ImDrawFlags corners_tl_br = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight;\n            const float rounding = sz / 5.0f;\n            const int circle_segments = circle_segments_override ? circle_segments_override_v : 0;\n            const int curve_segments = curve_segments_override ? curve_segments_override_v : 0;\n            float x = p.x + 4.0f;\n            float y = p.y + 4.0f;\n            for (int n = 0; n < 2; n++)\n            {\n                // First line uses a thickness of 1.0f, second line uses the configurable thickness\n                float th = (n == 0) ? 1.0f : thickness;\n                draw_list->AddNgon(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, ngon_sides, th);                 x += sz + spacing;  // N-gon\n                draw_list->AddCircle(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, col, circle_segments, th);          x += sz + spacing;  // Circle\n                draw_list->AddEllipse(ImVec2(x + sz*0.5f, y + sz*0.5f), sz*0.5f, sz*0.3f, col, -0.3f, circle_segments, th); x += sz + spacing;\t// Ellipse\n                draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 0.0f, ImDrawFlags_None, th);          x += sz + spacing;  // Square\n                draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, ImDrawFlags_None, th);      x += sz + spacing;  // Square with all rounded corners\n                draw_list->AddRect(ImVec2(x, y), ImVec2(x + sz, y + sz), col, rounding, corners_tl_br, th);         x += sz + spacing;  // Square with two rounded corners\n                draw_list->AddTriangle(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col, th);x += sz + spacing;  // Triangle\n                //draw_list->AddTriangle(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col, th);x+= sz*0.4f + spacing; // Thin triangle\n                draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y), col, th);                                       x += sz + spacing;  // Horizontal line (note: drawing a filled rectangle will be faster!)\n                draw_list->AddLine(ImVec2(x, y), ImVec2(x, y + sz), col, th);                                       x += spacing;       // Vertical line (note: drawing a filled rectangle will be faster!)\n                draw_list->AddLine(ImVec2(x, y), ImVec2(x + sz, y + sz), col, th);                                  x += sz + spacing;  // Diagonal line\n\n                // Quadratic Bezier Curve (3 control points)\n                ImVec2 cp3[3] = { ImVec2(x, y + sz * 0.6f), ImVec2(x + sz * 0.5f, y - sz * 0.4f), ImVec2(x + sz, y + sz) };\n                draw_list->AddBezierQuadratic(cp3[0], cp3[1], cp3[2], col, th, curve_segments); x += sz + spacing;\n\n                // Cubic Bezier Curve (4 control points)\n                ImVec2 cp4[4] = { ImVec2(x, y), ImVec2(x + sz * 1.3f, y + sz * 0.3f), ImVec2(x + sz - sz * 1.3f, y + sz - sz * 0.3f), ImVec2(x + sz, y + sz) };\n                draw_list->AddBezierCubic(cp4[0], cp4[1], cp4[2], cp4[3], col, th, curve_segments);\n\n                x = p.x + 4;\n                y += sz + spacing;\n            }\n            draw_list->AddNgonFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, ngon_sides);             x += sz + spacing;  // N-gon\n            draw_list->AddCircleFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, col, circle_segments);      x += sz + spacing;  // Circle\n            draw_list->AddEllipseFilled(ImVec2(x + sz * 0.5f, y + sz * 0.5f), sz * 0.5f, sz * 0.3f, col, -0.3f, circle_segments); x += sz + spacing;// Ellipse\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col);                                    x += sz + spacing;  // Square\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f);                             x += sz + spacing;  // Square with all rounded corners\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + sz), col, 10.0f, corners_tl_br);              x += sz + spacing;  // Square with two rounded corners\n            draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f,y), ImVec2(x+sz, y+sz-0.5f), ImVec2(x, y+sz-0.5f), col);  x += sz + spacing;  // Triangle\n            //draw_list->AddTriangleFilled(ImVec2(x+sz*0.2f,y), ImVec2(x, y+sz-0.5f), ImVec2(x+sz*0.4f, y+sz-0.5f), col); x += sz*0.4f + spacing; // Thin triangle\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + sz, y + thickness), col);                             x += sz + spacing;  // Horizontal line (faster than AddLine, but only handle integer thickness)\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + thickness, y + sz), col);                             x += spacing * 2.0f;// Vertical line (faster than AddLine, but only handle integer thickness)\n            draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x + 1, y + 1), col);                                      x += sz;            // Pixel (faster than AddLine)\n            draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x + sz, y + sz), IM_COL32(0, 0, 0, 255), IM_COL32(255, 0, 0, 255), IM_COL32(255, 255, 0, 255), IM_COL32(0, 255, 0, 255));\n\n            ImGui::Dummy(ImVec2((sz + spacing) * 11.2f, (sz + spacing) * 3.0f));\n            ImGui::PopItemWidth();\n            ImGui::EndTabItem();\n        }\n\n        if (ImGui::BeginTabItem(\"Canvas\"))\n        {\n            static ImVector<ImVec2> points;\n            static ImVec2 scrolling(0.0f, 0.0f);\n            static bool opt_enable_grid = true;\n            static bool opt_enable_context_menu = true;\n            static bool adding_line = false;\n\n            ImGui::Checkbox(\"Enable grid\", &opt_enable_grid);\n            ImGui::Checkbox(\"Enable context menu\", &opt_enable_context_menu);\n            ImGui::Text(\"Mouse Left: drag to add lines,\\nMouse Right: drag to scroll, click for context menu.\");\n\n            // Typically you would use a BeginChild()/EndChild() pair to benefit from a clipping region + own scrolling.\n            // Here we demonstrate that this can be replaced by simple offsetting + custom drawing + PushClipRect/PopClipRect() calls.\n            // To use a child window instead we could use, e.g:\n            //      ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));      // Disable padding\n            //      ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(50, 50, 50, 255));  // Set a background color\n            //      ImGui::BeginChild(\"canvas\", ImVec2(0.0f, 0.0f), ImGuiChildFlags_Border, ImGuiWindowFlags_NoMove);\n            //      ImGui::PopStyleColor();\n            //      ImGui::PopStyleVar();\n            //      [...]\n            //      ImGui::EndChild();\n\n            // Using InvisibleButton() as a convenience 1) it will advance the layout cursor and 2) allows us to use IsItemHovered()/IsItemActive()\n            ImVec2 canvas_p0 = ImGui::GetCursorScreenPos();      // ImDrawList API uses screen coordinates!\n            ImVec2 canvas_sz = ImGui::GetContentRegionAvail();   // Resize canvas to what's available\n            if (canvas_sz.x < 50.0f) canvas_sz.x = 50.0f;\n            if (canvas_sz.y < 50.0f) canvas_sz.y = 50.0f;\n            ImVec2 canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y);\n\n            // Draw border and background color\n            ImGuiIO& io = ImGui::GetIO();\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n            draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(50, 50, 50, 255));\n            draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255));\n\n            // This will catch our interactions\n            ImGui::InvisibleButton(\"canvas\", canvas_sz, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight);\n            const bool is_hovered = ImGui::IsItemHovered(); // Hovered\n            const bool is_active = ImGui::IsItemActive();   // Held\n            const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y); // Lock scrolled origin\n            const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, io.MousePos.y - origin.y);\n\n            // Add first and second point\n            if (is_hovered && !adding_line && ImGui::IsMouseClicked(ImGuiMouseButton_Left))\n            {\n                points.push_back(mouse_pos_in_canvas);\n                points.push_back(mouse_pos_in_canvas);\n                adding_line = true;\n            }\n            if (adding_line)\n            {\n                points.back() = mouse_pos_in_canvas;\n                if (!ImGui::IsMouseDown(ImGuiMouseButton_Left))\n                    adding_line = false;\n            }\n\n            // Pan (we use a zero mouse threshold when there's no context menu)\n            // You may decide to make that threshold dynamic based on whether the mouse is hovering something etc.\n            const float mouse_threshold_for_pan = opt_enable_context_menu ? -1.0f : 0.0f;\n            if (is_active && ImGui::IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan))\n            {\n                scrolling.x += io.MouseDelta.x;\n                scrolling.y += io.MouseDelta.y;\n            }\n\n            // Context menu (under default mouse threshold)\n            ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right);\n            if (opt_enable_context_menu && drag_delta.x == 0.0f && drag_delta.y == 0.0f)\n                ImGui::OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n            if (ImGui::BeginPopup(\"context\"))\n            {\n                if (adding_line)\n                    points.resize(points.size() - 2);\n                adding_line = false;\n                if (ImGui::MenuItem(\"Remove one\", NULL, false, points.Size > 0)) { points.resize(points.size() - 2); }\n                if (ImGui::MenuItem(\"Remove all\", NULL, false, points.Size > 0)) { points.clear(); }\n                ImGui::EndPopup();\n            }\n\n            // Draw grid + all lines in the canvas\n            draw_list->PushClipRect(canvas_p0, canvas_p1, true);\n            if (opt_enable_grid)\n            {\n                const float GRID_STEP = 64.0f;\n                for (float x = fmodf(scrolling.x, GRID_STEP); x < canvas_sz.x; x += GRID_STEP)\n                    draw_list->AddLine(ImVec2(canvas_p0.x + x, canvas_p0.y), ImVec2(canvas_p0.x + x, canvas_p1.y), IM_COL32(200, 200, 200, 40));\n                for (float y = fmodf(scrolling.y, GRID_STEP); y < canvas_sz.y; y += GRID_STEP)\n                    draw_list->AddLine(ImVec2(canvas_p0.x, canvas_p0.y + y), ImVec2(canvas_p1.x, canvas_p0.y + y), IM_COL32(200, 200, 200, 40));\n            }\n            for (int n = 0; n < points.Size; n += 2)\n                draw_list->AddLine(ImVec2(origin.x + points[n].x, origin.y + points[n].y), ImVec2(origin.x + points[n + 1].x, origin.y + points[n + 1].y), IM_COL32(255, 255, 0, 255), 2.0f);\n            draw_list->PopClipRect();\n\n            ImGui::EndTabItem();\n        }\n\n        if (ImGui::BeginTabItem(\"BG/FG draw lists\"))\n        {\n            static bool draw_bg = true;\n            static bool draw_fg = true;\n            ImGui::Checkbox(\"Draw in Background draw list\", &draw_bg);\n            ImGui::SameLine(); HelpMarker(\"The Background draw list will be rendered below every Dear ImGui windows.\");\n            ImGui::Checkbox(\"Draw in Foreground draw list\", &draw_fg);\n            ImGui::SameLine(); HelpMarker(\"The Foreground draw list will be rendered over every Dear ImGui windows.\");\n            ImVec2 window_pos = ImGui::GetWindowPos();\n            ImVec2 window_size = ImGui::GetWindowSize();\n            ImVec2 window_center = ImVec2(window_pos.x + window_size.x * 0.5f, window_pos.y + window_size.y * 0.5f);\n            if (draw_bg)\n                ImGui::GetBackgroundDrawList()->AddCircle(window_center, window_size.x * 0.6f, IM_COL32(255, 0, 0, 200), 0, 10 + 4);\n            if (draw_fg)\n                ImGui::GetForegroundDrawList()->AddCircle(window_center, window_size.y * 0.6f, IM_COL32(0, 255, 0, 200), 0, 10);\n            ImGui::EndTabItem();\n        }\n\n        // Demonstrate out-of-order rendering via channels splitting\n        // We use functions in ImDrawList as each draw list contains a convenience splitter,\n        // but you can also instantiate your own ImDrawListSplitter if you need to nest them.\n        if (ImGui::BeginTabItem(\"Draw Channels\"))\n        {\n            ImDrawList* draw_list = ImGui::GetWindowDrawList();\n            {\n                ImGui::Text(\"Blue shape is drawn first: appears in back\");\n                ImGui::Text(\"Red shape is drawn after: appears in front\");\n                ImVec2 p0 = ImGui::GetCursorScreenPos();\n                draw_list->AddRectFilled(ImVec2(p0.x, p0.y), ImVec2(p0.x + 50, p0.y + 50), IM_COL32(0, 0, 255, 255)); // Blue\n                draw_list->AddRectFilled(ImVec2(p0.x + 25, p0.y + 25), ImVec2(p0.x + 75, p0.y + 75), IM_COL32(255, 0, 0, 255)); // Red\n                ImGui::Dummy(ImVec2(75, 75));\n            }\n            ImGui::Separator();\n            {\n                ImGui::Text(\"Blue shape is drawn first, into channel 1: appears in front\");\n                ImGui::Text(\"Red shape is drawn after, into channel 0: appears in back\");\n                ImVec2 p1 = ImGui::GetCursorScreenPos();\n\n                // Create 2 channels and draw a Blue shape THEN a Red shape.\n                // You can create any number of channels. Tables API use 1 channel per column in order to better batch draw calls.\n                draw_list->ChannelsSplit(2);\n                draw_list->ChannelsSetCurrent(1);\n                draw_list->AddRectFilled(ImVec2(p1.x, p1.y), ImVec2(p1.x + 50, p1.y + 50), IM_COL32(0, 0, 255, 255)); // Blue\n                draw_list->ChannelsSetCurrent(0);\n                draw_list->AddRectFilled(ImVec2(p1.x + 25, p1.y + 25), ImVec2(p1.x + 75, p1.y + 75), IM_COL32(255, 0, 0, 255)); // Red\n\n                // Flatten/reorder channels. Red shape is in channel 0 and it appears below the Blue shape in channel 1.\n                // This works by copying draw indices only (vertices are not copied).\n                draw_list->ChannelsMerge();\n                ImGui::Dummy(ImVec2(75, 75));\n                ImGui::Text(\"After reordering, contents of channel 0 appears below channel 1.\");\n            }\n            ImGui::EndTabItem();\n        }\n\n        ImGui::EndTabBar();\n    }\n\n    ImGui::End();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Example App: Documents Handling / ShowExampleAppDocuments()\n//-----------------------------------------------------------------------------\n\n// Simplified structure to mimic a Document model\nstruct MyDocument\n{\n    const char* Name;       // Document title\n    bool        Open;       // Set when open (we keep an array of all available documents to simplify demo code!)\n    bool        OpenPrev;   // Copy of Open from last update.\n    bool        Dirty;      // Set when the document has been modified\n    bool        WantClose;  // Set when the document\n    ImVec4      Color;      // An arbitrary variable associated to the document\n\n    MyDocument(const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f))\n    {\n        Name = name;\n        Open = OpenPrev = open;\n        Dirty = false;\n        WantClose = false;\n        Color = color;\n    }\n    void DoOpen()       { Open = true; }\n    void DoQueueClose() { WantClose = true; }\n    void DoForceClose() { Open = false; Dirty = false; }\n    void DoSave()       { Dirty = false; }\n\n    // Display placeholder contents for the Document\n    static void DisplayContents(MyDocument* doc)\n    {\n        ImGui::PushID(doc);\n        ImGui::Text(\"Document \\\"%s\\\"\", doc->Name);\n        ImGui::PushStyleColor(ImGuiCol_Text, doc->Color);\n        ImGui::TextWrapped(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\");\n        ImGui::PopStyleColor();\n        if (ImGui::Button(\"Modify\", ImVec2(100, 0)))\n            doc->Dirty = true;\n        ImGui::SameLine();\n        if (ImGui::Button(\"Save\", ImVec2(100, 0)))\n            doc->DoSave();\n        ImGui::ColorEdit3(\"color\", &doc->Color.x);  // Useful to test drag and drop and hold-dragged-to-open-tab behavior.\n        ImGui::PopID();\n    }\n\n    // Display context menu for the Document\n    static void DisplayContextMenu(MyDocument* doc)\n    {\n        if (!ImGui::BeginPopupContextItem())\n            return;\n\n        char buf[256];\n        sprintf(buf, \"Save %s\", doc->Name);\n        if (ImGui::MenuItem(buf, \"CTRL+S\", false, doc->Open))\n            doc->DoSave();\n        if (ImGui::MenuItem(\"Close\", \"CTRL+W\", false, doc->Open))\n            doc->DoQueueClose();\n        ImGui::EndPopup();\n    }\n};\n\nstruct ExampleAppDocuments\n{\n    ImVector<MyDocument> Documents;\n\n    ExampleAppDocuments()\n    {\n        Documents.push_back(MyDocument(\"Lettuce\",             true,  ImVec4(0.4f, 0.8f, 0.4f, 1.0f)));\n        Documents.push_back(MyDocument(\"Eggplant\",            true,  ImVec4(0.8f, 0.5f, 1.0f, 1.0f)));\n        Documents.push_back(MyDocument(\"Carrot\",              true,  ImVec4(1.0f, 0.8f, 0.5f, 1.0f)));\n        Documents.push_back(MyDocument(\"Tomato\",              false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f)));\n        Documents.push_back(MyDocument(\"A Rather Long Title\", false));\n        Documents.push_back(MyDocument(\"Some Document\",       false));\n    }\n};\n\n// [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.\n// If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,\n// as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for\n// the tab bar to notice its absence. During this frame there will be a gap in the tab bar, and if the tab that has\n// disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively\n// give the impression of a flicker for one frame.\n// We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.\n// Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag.\nstatic void NotifyOfDocumentsClosedElsewhere(ExampleAppDocuments& app)\n{\n    for (MyDocument& doc : app.Documents)\n    {\n        if (!doc.Open && doc.OpenPrev)\n            ImGui::SetTabItemClosed(doc.Name);\n        doc.OpenPrev = doc.Open;\n    }\n}\n\nvoid ShowExampleAppDocuments(bool* p_open)\n{\n    static ExampleAppDocuments app;\n\n    // Options\n    static bool opt_reorderable = true;\n    static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_;\n\n    bool window_contents_visible = ImGui::Begin(\"Example: Documents\", p_open, ImGuiWindowFlags_MenuBar);\n    if (!window_contents_visible)\n    {\n        ImGui::End();\n        return;\n    }\n\n    // Menu\n    if (ImGui::BeginMenuBar())\n    {\n        if (ImGui::BeginMenu(\"File\"))\n        {\n            int open_count = 0;\n            for (MyDocument& doc : app.Documents)\n                open_count += doc.Open ? 1 : 0;\n\n            if (ImGui::BeginMenu(\"Open\", open_count < app.Documents.Size))\n            {\n                for (MyDocument& doc : app.Documents)\n                    if (!doc.Open && ImGui::MenuItem(doc.Name))\n                        doc.DoOpen();\n                ImGui::EndMenu();\n            }\n            if (ImGui::MenuItem(\"Close All Documents\", NULL, false, open_count > 0))\n                for (MyDocument& doc : app.Documents)\n                    doc.DoQueueClose();\n            if (ImGui::MenuItem(\"Exit\", \"Ctrl+F4\") && p_open)\n                *p_open = false;\n            ImGui::EndMenu();\n        }\n        ImGui::EndMenuBar();\n    }\n\n    // [Debug] List documents with one checkbox for each\n    for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++)\n    {\n        MyDocument& doc = app.Documents[doc_n];\n        if (doc_n > 0)\n            ImGui::SameLine();\n        ImGui::PushID(&doc);\n        if (ImGui::Checkbox(doc.Name, &doc.Open))\n            if (!doc.Open)\n                doc.DoForceClose();\n        ImGui::PopID();\n    }\n\n    ImGui::Separator();\n\n    // About the ImGuiWindowFlags_UnsavedDocument / ImGuiTabItemFlags_UnsavedDocument flags.\n    // They have multiple effects:\n    // - Display a dot next to the title.\n    // - Tab is selected when clicking the X close button.\n    // - Closure is not assumed (will wait for user to stop submitting the tab).\n    //   Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar.\n    //   We need to assume closure by default otherwise waiting for \"lack of submission\" on the next frame would leave an empty\n    //   hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window.\n    //   The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole.\n\n    // Submit Tab Bar and Tabs\n    {\n        ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0);\n        if (ImGui::BeginTabBar(\"##tabs\", tab_bar_flags))\n        {\n            if (opt_reorderable)\n                NotifyOfDocumentsClosedElsewhere(app);\n\n            // [DEBUG] Stress tests\n            //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1;            // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on.\n            //if (ImGui::GetIO().KeyCtrl) ImGui::SetTabItemSelected(docs[1].Name);  // [DEBUG] Test SetTabItemSelected(), probably not very useful as-is anyway..\n\n            // Submit Tabs\n            for (MyDocument& doc : app.Documents)\n            {\n                if (!doc.Open)\n                    continue;\n\n                ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0);\n                bool visible = ImGui::BeginTabItem(doc.Name, &doc.Open, tab_flags);\n\n                // Cancel attempt to close when unsaved add to save queue so we can display a popup.\n                if (!doc.Open && doc.Dirty)\n                {\n                    doc.Open = true;\n                    doc.DoQueueClose();\n                }\n\n                MyDocument::DisplayContextMenu(&doc);\n                if (visible)\n                {\n                    MyDocument::DisplayContents(&doc);\n                    ImGui::EndTabItem();\n                }\n            }\n\n            ImGui::EndTabBar();\n        }\n    }\n\n    // Update closing queue\n    static ImVector<MyDocument*> close_queue;\n    if (close_queue.empty())\n    {\n        // Close queue is locked once we started a popup\n        for (MyDocument& doc : app.Documents)\n            if (doc.WantClose)\n            {\n                doc.WantClose = false;\n                close_queue.push_back(&doc);\n            }\n    }\n\n    // Display closing confirmation UI\n    if (!close_queue.empty())\n    {\n        int close_queue_unsaved_documents = 0;\n        for (int n = 0; n < close_queue.Size; n++)\n            if (close_queue[n]->Dirty)\n                close_queue_unsaved_documents++;\n\n        if (close_queue_unsaved_documents == 0)\n        {\n            // Close documents when all are unsaved\n            for (int n = 0; n < close_queue.Size; n++)\n                close_queue[n]->DoForceClose();\n            close_queue.clear();\n        }\n        else\n        {\n            if (!ImGui::IsPopupOpen(\"Save?\"))\n                ImGui::OpenPopup(\"Save?\");\n            if (ImGui::BeginPopupModal(\"Save?\", NULL, ImGuiWindowFlags_AlwaysAutoResize))\n            {\n                ImGui::Text(\"Save change to the following items?\");\n                float item_height = ImGui::GetTextLineHeightWithSpacing();\n                if (ImGui::BeginChild(ImGui::GetID(\"frame\"), ImVec2(-FLT_MIN, 6.25f * item_height), ImGuiChildFlags_FrameStyle))\n                {\n                    for (int n = 0; n < close_queue.Size; n++)\n                        if (close_queue[n]->Dirty)\n                            ImGui::Text(\"%s\", close_queue[n]->Name);\n                }\n                ImGui::EndChild();\n\n                ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f);\n                if (ImGui::Button(\"Yes\", button_size))\n                {\n                    for (int n = 0; n < close_queue.Size; n++)\n                    {\n                        if (close_queue[n]->Dirty)\n                            close_queue[n]->DoSave();\n                        close_queue[n]->DoForceClose();\n                    }\n                    close_queue.clear();\n                    ImGui::CloseCurrentPopup();\n                }\n                ImGui::SameLine();\n                if (ImGui::Button(\"No\", button_size))\n                {\n                    for (int n = 0; n < close_queue.Size; n++)\n                        close_queue[n]->DoForceClose();\n                    close_queue.clear();\n                    ImGui::CloseCurrentPopup();\n                }\n                ImGui::SameLine();\n                if (ImGui::Button(\"Cancel\", button_size))\n                {\n                    close_queue.clear();\n                    ImGui::CloseCurrentPopup();\n                }\n                ImGui::EndPopup();\n            }\n        }\n    }\n\n    ImGui::End();\n}\n\n// End of Demo code\n#else\n\nvoid ImGui::ShowAboutWindow(bool*) {}\nvoid ImGui::ShowDemoWindow(bool*) {}\nvoid ImGui::ShowUserGuide() {}\nvoid ImGui::ShowStyleEditor(ImGuiStyle*) {}\n\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "third-party/imgui/imgui_draw.cpp",
    "content": "// dear imgui, v1.90.0\n// (drawing and font code)\n\n/*\n\nIndex of this file:\n\n// [SECTION] STB libraries implementation\n// [SECTION] Style functions\n// [SECTION] ImDrawList\n// [SECTION] ImDrawListSplitter\n// [SECTION] ImDrawData\n// [SECTION] Helpers ShadeVertsXXX functions\n// [SECTION] ImFontConfig\n// [SECTION] ImFontAtlas\n// [SECTION] ImFontAtlas glyph ranges helpers\n// [SECTION] ImFontGlyphRangesBuilder\n// [SECTION] ImFont\n// [SECTION] ImGui Internal Render Helpers\n// [SECTION] Decompression code\n// [SECTION] Default font data (ProggyClean.ttf)\n\n*/\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#ifndef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS\n#endif\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_internal.h\"\n#ifdef IMGUI_ENABLE_FREETYPE\n#include \"misc/freetype/imgui_freetype.h\"\n#endif\n\n#include <stdio.h>      // vsnprintf, sscanf, printf\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4127)     // condition expression is constant\n#pragma warning (disable: 4505)     // unreferenced local function has been removed (stb stuff)\n#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).\n#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast                            // yes, they are more terse.\n#pragma clang diagnostic ignored \"-Wfloat-equal\"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok.\n#pragma clang diagnostic ignored \"-Wglobal-constructors\"            // warning: declaration requires a global destructor         // similar to above, not sure what the exact difference is.\n#pragma clang diagnostic ignored \"-Wsign-conversion\"                // warning: implicit conversion changes signedness\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0\n#pragma clang diagnostic ignored \"-Wcomma\"                          // warning: possible misuse of comma operator here\n#pragma clang diagnostic ignored \"-Wreserved-id-macro\"              // warning: macro name is a reserved identifier\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#pragma clang diagnostic ignored \"-Wreserved-identifier\"            // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter\n#elif defined(__GNUC__)\n#pragma GCC diagnostic ignored \"-Wpragmas\"                  // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wunused-function\"          // warning: 'xxxx' defined but not used\n#pragma GCC diagnostic ignored \"-Wdouble-promotion\"         // warning: implicit conversion from 'float' to 'double' when passing argument to function\n#pragma GCC diagnostic ignored \"-Wconversion\"               // warning: conversion to 'xxxx' from 'xxxx' may alter its value\n#pragma GCC diagnostic ignored \"-Wstack-protector\"          // warning: stack protector not protecting local variables: variable length buffer\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"          // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#endif\n\n//-------------------------------------------------------------------------\n// [SECTION] STB libraries implementation (for stb_truetype and stb_rect_pack)\n//-------------------------------------------------------------------------\n\n// Compile time options:\n//#define IMGUI_STB_NAMESPACE           ImStb\n//#define IMGUI_STB_TRUETYPE_FILENAME   \"my_folder/stb_truetype.h\"\n//#define IMGUI_STB_RECT_PACK_FILENAME  \"my_folder/stb_rect_pack.h\"\n//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION\n//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION\n\n#ifdef IMGUI_STB_NAMESPACE\nnamespace IMGUI_STB_NAMESPACE\n{\n#endif\n\n#ifdef _MSC_VER\n#pragma warning (push)\n#pragma warning (disable: 4456)                             // declaration of 'xx' hides previous local declaration\n#pragma warning (disable: 6011)                             // (stb_rectpack) Dereferencing NULL pointer 'cur->next'.\n#pragma warning (disable: 6385)                             // (stb_truetype) Reading invalid data from 'buffer':  the readable size is '_Old_3`kernel_width' bytes, but '3' bytes may be read.\n#pragma warning (disable: 28182)                            // (stb_rectpack) Dereferencing NULL pointer. 'cur' contains the same NULL value as 'cur->next' did.\n#endif\n\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wunused-function\"\n#pragma clang diagnostic ignored \"-Wmissing-prototypes\"\n#pragma clang diagnostic ignored \"-Wimplicit-fallthrough\"\n#pragma clang diagnostic ignored \"-Wcast-qual\"              // warning: cast from 'const xxxx *' to 'xxx *' drops const qualifier\n#endif\n\n#if defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wtype-limits\"              // warning: comparison is always true due to limited range of data type [-Wtype-limits]\n#pragma GCC diagnostic ignored \"-Wcast-qual\"                // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers\n#endif\n\n#ifndef STB_RECT_PACK_IMPLEMENTATION                        // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)\n#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION          // in case the user already have an implementation in another compilation unit\n#define STBRP_STATIC\n#define STBRP_ASSERT(x)     do { IM_ASSERT(x); } while (0)\n#define STBRP_SORT          ImQsort\n#define STB_RECT_PACK_IMPLEMENTATION\n#endif\n#ifdef IMGUI_STB_RECT_PACK_FILENAME\n#include IMGUI_STB_RECT_PACK_FILENAME\n#else\n#include \"imstb_rectpack.h\"\n#endif\n#endif\n\n#ifdef  IMGUI_ENABLE_STB_TRUETYPE\n#ifndef STB_TRUETYPE_IMPLEMENTATION                         // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds)\n#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION           // in case the user already have an implementation in another compilation unit\n#define STBTT_malloc(x,u)   ((void)(u), IM_ALLOC(x))\n#define STBTT_free(x,u)     ((void)(u), IM_FREE(x))\n#define STBTT_assert(x)     do { IM_ASSERT(x); } while(0)\n#define STBTT_fmod(x,y)     ImFmod(x,y)\n#define STBTT_sqrt(x)       ImSqrt(x)\n#define STBTT_pow(x,y)      ImPow(x,y)\n#define STBTT_fabs(x)       ImFabs(x)\n#define STBTT_ifloor(x)     ((int)ImFloor(x))\n#define STBTT_iceil(x)      ((int)ImCeil(x))\n#define STBTT_STATIC\n#define STB_TRUETYPE_IMPLEMENTATION\n#else\n#define STBTT_DEF extern\n#endif\n#ifdef IMGUI_STB_TRUETYPE_FILENAME\n#include IMGUI_STB_TRUETYPE_FILENAME\n#else\n#include \"imstb_truetype.h\"\n#endif\n#endif\n#endif // IMGUI_ENABLE_STB_TRUETYPE\n\n#if defined(__GNUC__)\n#pragma GCC diagnostic pop\n#endif\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n\n#if defined(_MSC_VER)\n#pragma warning (pop)\n#endif\n\n#ifdef IMGUI_STB_NAMESPACE\n} // namespace ImStb\nusing namespace IMGUI_STB_NAMESPACE;\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Style functions\n//-----------------------------------------------------------------------------\n\nvoid ImGui::StyleColorsDark(ImGuiStyle* dst)\n{\n    ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();\n    ImVec4* colors = style->Colors;\n\n    colors[ImGuiCol_Text]                   = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImGuiCol_TextDisabled]           = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);\n    colors[ImGuiCol_WindowBg]               = ImVec4(0.06f, 0.06f, 0.06f, 0.94f);\n    colors[ImGuiCol_ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_PopupBg]                = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);\n    colors[ImGuiCol_Border]                 = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);\n    colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_FrameBg]                = ImVec4(0.16f, 0.29f, 0.48f, 0.54f);\n    colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);\n    colors[ImGuiCol_FrameBgActive]          = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);\n    colors[ImGuiCol_TitleBg]                = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);\n    colors[ImGuiCol_TitleBgActive]          = ImVec4(0.16f, 0.29f, 0.48f, 1.00f);\n    colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);\n    colors[ImGuiCol_MenuBarBg]              = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);\n    colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);\n    colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);\n    colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);\n    colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);\n    colors[ImGuiCol_CheckMark]              = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_SliderGrab]             = ImVec4(0.24f, 0.52f, 0.88f, 1.00f);\n    colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_Button]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);\n    colors[ImGuiCol_ButtonHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_ButtonActive]           = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);\n    colors[ImGuiCol_Header]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);\n    colors[ImGuiCol_HeaderHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);\n    colors[ImGuiCol_HeaderActive]           = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_Separator]              = colors[ImGuiCol_Border];\n    colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.10f, 0.40f, 0.75f, 0.78f);\n    colors[ImGuiCol_SeparatorActive]        = ImVec4(0.10f, 0.40f, 0.75f, 1.00f);\n    colors[ImGuiCol_ResizeGrip]             = ImVec4(0.26f, 0.59f, 0.98f, 0.20f);\n    colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);\n    colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);\n    colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.80f);\n    colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered];\n    colors[ImGuiCol_TabActive]              = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);\n    colors[ImGuiCol_TabUnfocused]           = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);\n    colors[ImGuiCol_TabUnfocusedActive]     = ImLerp(colors[ImGuiCol_TabActive],    colors[ImGuiCol_TitleBg], 0.40f);\n    colors[ImGuiCol_PlotLines]              = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);\n    colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);\n    colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);\n    colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);\n    colors[ImGuiCol_TableHeaderBg]          = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);\n    colors[ImGuiCol_TableBorderStrong]      = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableBorderLight]       = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_TableRowBgAlt]          = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);\n    colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);\n    colors[ImGuiCol_DragDropTarget]         = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);\n    colors[ImGuiCol_NavHighlight]           = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);\n    colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);\n    colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);\n}\n\nvoid ImGui::StyleColorsClassic(ImGuiStyle* dst)\n{\n    ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();\n    ImVec4* colors = style->Colors;\n\n    colors[ImGuiCol_Text]                   = ImVec4(0.90f, 0.90f, 0.90f, 1.00f);\n    colors[ImGuiCol_TextDisabled]           = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);\n    colors[ImGuiCol_WindowBg]               = ImVec4(0.00f, 0.00f, 0.00f, 0.85f);\n    colors[ImGuiCol_ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_PopupBg]                = ImVec4(0.11f, 0.11f, 0.14f, 0.92f);\n    colors[ImGuiCol_Border]                 = ImVec4(0.50f, 0.50f, 0.50f, 0.50f);\n    colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_FrameBg]                = ImVec4(0.43f, 0.43f, 0.43f, 0.39f);\n    colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.47f, 0.47f, 0.69f, 0.40f);\n    colors[ImGuiCol_FrameBgActive]          = ImVec4(0.42f, 0.41f, 0.64f, 0.69f);\n    colors[ImGuiCol_TitleBg]                = ImVec4(0.27f, 0.27f, 0.54f, 0.83f);\n    colors[ImGuiCol_TitleBgActive]          = ImVec4(0.32f, 0.32f, 0.63f, 0.87f);\n    colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(0.40f, 0.40f, 0.80f, 0.20f);\n    colors[ImGuiCol_MenuBarBg]              = ImVec4(0.40f, 0.40f, 0.55f, 0.80f);\n    colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);\n    colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.40f, 0.40f, 0.80f, 0.30f);\n    colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.40f, 0.40f, 0.80f, 0.40f);\n    colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.41f, 0.39f, 0.80f, 0.60f);\n    colors[ImGuiCol_CheckMark]              = ImVec4(0.90f, 0.90f, 0.90f, 0.50f);\n    colors[ImGuiCol_SliderGrab]             = ImVec4(1.00f, 1.00f, 1.00f, 0.30f);\n    colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.41f, 0.39f, 0.80f, 0.60f);\n    colors[ImGuiCol_Button]                 = ImVec4(0.35f, 0.40f, 0.61f, 0.62f);\n    colors[ImGuiCol_ButtonHovered]          = ImVec4(0.40f, 0.48f, 0.71f, 0.79f);\n    colors[ImGuiCol_ButtonActive]           = ImVec4(0.46f, 0.54f, 0.80f, 1.00f);\n    colors[ImGuiCol_Header]                 = ImVec4(0.40f, 0.40f, 0.90f, 0.45f);\n    colors[ImGuiCol_HeaderHovered]          = ImVec4(0.45f, 0.45f, 0.90f, 0.80f);\n    colors[ImGuiCol_HeaderActive]           = ImVec4(0.53f, 0.53f, 0.87f, 0.80f);\n    colors[ImGuiCol_Separator]              = ImVec4(0.50f, 0.50f, 0.50f, 0.60f);\n    colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.60f, 0.60f, 0.70f, 1.00f);\n    colors[ImGuiCol_SeparatorActive]        = ImVec4(0.70f, 0.70f, 0.90f, 1.00f);\n    colors[ImGuiCol_ResizeGrip]             = ImVec4(1.00f, 1.00f, 1.00f, 0.10f);\n    colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);\n    colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.78f, 0.82f, 1.00f, 0.90f);\n    colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.80f);\n    colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered];\n    colors[ImGuiCol_TabActive]              = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);\n    colors[ImGuiCol_TabUnfocused]           = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);\n    colors[ImGuiCol_TabUnfocusedActive]     = ImLerp(colors[ImGuiCol_TabActive],    colors[ImGuiCol_TitleBg], 0.40f);\n    colors[ImGuiCol_PlotLines]              = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImGuiCol_PlotLinesHovered]       = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);\n    colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);\n    colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);\n    colors[ImGuiCol_TableHeaderBg]          = ImVec4(0.27f, 0.27f, 0.38f, 1.00f);\n    colors[ImGuiCol_TableBorderStrong]      = ImVec4(0.31f, 0.31f, 0.45f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableBorderLight]       = ImVec4(0.26f, 0.26f, 0.28f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_TableRowBgAlt]          = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);\n    colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);\n    colors[ImGuiCol_DragDropTarget]         = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);\n    colors[ImGuiCol_NavHighlight]           = colors[ImGuiCol_HeaderHovered];\n    colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);\n    colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);\n    colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);\n}\n\n// Those light colors are better suited with a thicker font than the default one + FrameBorder\nvoid ImGui::StyleColorsLight(ImGuiStyle* dst)\n{\n    ImGuiStyle* style = dst ? dst : &ImGui::GetStyle();\n    ImVec4* colors = style->Colors;\n\n    colors[ImGuiCol_Text]                   = ImVec4(0.00f, 0.00f, 0.00f, 1.00f);\n    colors[ImGuiCol_TextDisabled]           = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);\n    colors[ImGuiCol_WindowBg]               = ImVec4(0.94f, 0.94f, 0.94f, 1.00f);\n    colors[ImGuiCol_ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_PopupBg]                = ImVec4(1.00f, 1.00f, 1.00f, 0.98f);\n    colors[ImGuiCol_Border]                 = ImVec4(0.00f, 0.00f, 0.00f, 0.30f);\n    colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_FrameBg]                = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n    colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);\n    colors[ImGuiCol_FrameBgActive]          = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);\n    colors[ImGuiCol_TitleBg]                = ImVec4(0.96f, 0.96f, 0.96f, 1.00f);\n    colors[ImGuiCol_TitleBgActive]          = ImVec4(0.82f, 0.82f, 0.82f, 1.00f);\n    colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(1.00f, 1.00f, 1.00f, 0.51f);\n    colors[ImGuiCol_MenuBarBg]              = ImVec4(0.86f, 0.86f, 0.86f, 1.00f);\n    colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.98f, 0.98f, 0.98f, 0.53f);\n    colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.69f, 0.69f, 0.69f, 0.80f);\n    colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.49f, 0.49f, 0.49f, 0.80f);\n    colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.49f, 0.49f, 0.49f, 1.00f);\n    colors[ImGuiCol_CheckMark]              = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_SliderGrab]             = ImVec4(0.26f, 0.59f, 0.98f, 0.78f);\n    colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.46f, 0.54f, 0.80f, 0.60f);\n    colors[ImGuiCol_Button]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);\n    colors[ImGuiCol_ButtonHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_ButtonActive]           = ImVec4(0.06f, 0.53f, 0.98f, 1.00f);\n    colors[ImGuiCol_Header]                 = ImVec4(0.26f, 0.59f, 0.98f, 0.31f);\n    colors[ImGuiCol_HeaderHovered]          = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);\n    colors[ImGuiCol_HeaderActive]           = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n    colors[ImGuiCol_Separator]              = ImVec4(0.39f, 0.39f, 0.39f, 0.62f);\n    colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.14f, 0.44f, 0.80f, 0.78f);\n    colors[ImGuiCol_SeparatorActive]        = ImVec4(0.14f, 0.44f, 0.80f, 1.00f);\n    colors[ImGuiCol_ResizeGrip]             = ImVec4(0.35f, 0.35f, 0.35f, 0.17f);\n    colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);\n    colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);\n    colors[ImGuiCol_Tab]                    = ImLerp(colors[ImGuiCol_Header],       colors[ImGuiCol_TitleBgActive], 0.90f);\n    colors[ImGuiCol_TabHovered]             = colors[ImGuiCol_HeaderHovered];\n    colors[ImGuiCol_TabActive]              = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);\n    colors[ImGuiCol_TabUnfocused]           = ImLerp(colors[ImGuiCol_Tab],          colors[ImGuiCol_TitleBg], 0.80f);\n    colors[ImGuiCol_TabUnfocusedActive]     = ImLerp(colors[ImGuiCol_TabActive],    colors[ImGuiCol_TitleBg], 0.40f);\n    colors[ImGuiCol_PlotLines]              = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);\n    colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);\n    colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);\n    colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.45f, 0.00f, 1.00f);\n    colors[ImGuiCol_TableHeaderBg]          = ImVec4(0.78f, 0.87f, 0.98f, 1.00f);\n    colors[ImGuiCol_TableBorderStrong]      = ImVec4(0.57f, 0.57f, 0.64f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableBorderLight]       = ImVec4(0.68f, 0.68f, 0.74f, 1.00f);   // Prefer using Alpha=1.0 here\n    colors[ImGuiCol_TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n    colors[ImGuiCol_TableRowBgAlt]          = ImVec4(0.30f, 0.30f, 0.30f, 0.09f);\n    colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);\n    colors[ImGuiCol_DragDropTarget]         = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);\n    colors[ImGuiCol_NavHighlight]           = colors[ImGuiCol_HeaderHovered];\n    colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(0.70f, 0.70f, 0.70f, 0.70f);\n    colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.20f, 0.20f, 0.20f, 0.20f);\n    colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImDrawList\n//-----------------------------------------------------------------------------\n\nImDrawListSharedData::ImDrawListSharedData()\n{\n    memset(this, 0, sizeof(*this));\n    for (int i = 0; i < IM_ARRAYSIZE(ArcFastVtx); i++)\n    {\n        const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(ArcFastVtx);\n        ArcFastVtx[i] = ImVec2(ImCos(a), ImSin(a));\n    }\n    ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);\n}\n\nvoid ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)\n{\n    if (CircleSegmentMaxError == max_error)\n        return;\n\n    IM_ASSERT(max_error > 0.0f);\n    CircleSegmentMaxError = max_error;\n    for (int i = 0; i < IM_ARRAYSIZE(CircleSegmentCounts); i++)\n    {\n        const float radius = (float)i;\n        CircleSegmentCounts[i] = (ImU8)((i > 0) ? IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, CircleSegmentMaxError) : IM_DRAWLIST_ARCFAST_SAMPLE_MAX);\n    }\n    ArcFastRadiusCutoff = IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(IM_DRAWLIST_ARCFAST_SAMPLE_MAX, CircleSegmentMaxError);\n}\n\n// Initialize before use in a new frame. We always have a command ready in the buffer.\nvoid ImDrawList::_ResetForNewFrame()\n{\n    // Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.\n    IM_STATIC_ASSERT(offsetof(ImDrawCmd, ClipRect) == 0);\n    IM_STATIC_ASSERT(offsetof(ImDrawCmd, TextureId) == sizeof(ImVec4));\n    IM_STATIC_ASSERT(offsetof(ImDrawCmd, VtxOffset) == sizeof(ImVec4) + sizeof(ImTextureID));\n    if (_Splitter._Count > 1)\n        _Splitter.Merge(this);\n\n    CmdBuffer.resize(0);\n    IdxBuffer.resize(0);\n    VtxBuffer.resize(0);\n    Flags = _Data->InitialFlags;\n    memset(&_CmdHeader, 0, sizeof(_CmdHeader));\n    _VtxCurrentIdx = 0;\n    _VtxWritePtr = NULL;\n    _IdxWritePtr = NULL;\n    _ClipRectStack.resize(0);\n    _TextureIdStack.resize(0);\n    _Path.resize(0);\n    _Splitter.Clear();\n    CmdBuffer.push_back(ImDrawCmd());\n    _FringeScale = 1.0f;\n}\n\nvoid ImDrawList::_ClearFreeMemory()\n{\n    CmdBuffer.clear();\n    IdxBuffer.clear();\n    VtxBuffer.clear();\n    Flags = ImDrawListFlags_None;\n    _VtxCurrentIdx = 0;\n    _VtxWritePtr = NULL;\n    _IdxWritePtr = NULL;\n    _ClipRectStack.clear();\n    _TextureIdStack.clear();\n    _Path.clear();\n    _Splitter.ClearFreeMemory();\n}\n\nImDrawList* ImDrawList::CloneOutput() const\n{\n    ImDrawList* dst = IM_NEW(ImDrawList(_Data));\n    dst->CmdBuffer = CmdBuffer;\n    dst->IdxBuffer = IdxBuffer;\n    dst->VtxBuffer = VtxBuffer;\n    dst->Flags = Flags;\n    return dst;\n}\n\nvoid ImDrawList::AddDrawCmd()\n{\n    ImDrawCmd draw_cmd;\n    draw_cmd.ClipRect = _CmdHeader.ClipRect;    // Same as calling ImDrawCmd_HeaderCopy()\n    draw_cmd.TextureId = _CmdHeader.TextureId;\n    draw_cmd.VtxOffset = _CmdHeader.VtxOffset;\n    draw_cmd.IdxOffset = IdxBuffer.Size;\n\n    IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w);\n    CmdBuffer.push_back(draw_cmd);\n}\n\n// Pop trailing draw command (used before merging or presenting to user)\n// Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL\nvoid ImDrawList::_PopUnusedDrawCmd()\n{\n    while (CmdBuffer.Size > 0)\n    {\n        ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n        if (curr_cmd->ElemCount != 0 || curr_cmd->UserCallback != NULL)\n            return;// break;\n        CmdBuffer.pop_back();\n    }\n}\n\nvoid ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)\n{\n    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);\n    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    IM_ASSERT(curr_cmd->UserCallback == NULL);\n    if (curr_cmd->ElemCount != 0)\n    {\n        AddDrawCmd();\n        curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    }\n    curr_cmd->UserCallback = callback;\n    curr_cmd->UserCallbackData = callback_data;\n\n    AddDrawCmd(); // Force a new command after us (see comment below)\n}\n\n// Compare ClipRect, TextureId and VtxOffset with a single memcmp()\n#define ImDrawCmd_HeaderSize                            (offsetof(ImDrawCmd, VtxOffset) + sizeof(unsigned int))\n#define ImDrawCmd_HeaderCompare(CMD_LHS, CMD_RHS)       (memcmp(CMD_LHS, CMD_RHS, ImDrawCmd_HeaderSize))    // Compare ClipRect, TextureId, VtxOffset\n#define ImDrawCmd_HeaderCopy(CMD_DST, CMD_SRC)          (memcpy(CMD_DST, CMD_SRC, ImDrawCmd_HeaderSize))    // Copy ClipRect, TextureId, VtxOffset\n#define ImDrawCmd_AreSequentialIdxOffset(CMD_0, CMD_1)  (CMD_0->IdxOffset + CMD_0->ElemCount == CMD_1->IdxOffset)\n\n// Try to merge two last draw commands\nvoid ImDrawList::_TryMergeDrawCmds()\n{\n    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);\n    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    ImDrawCmd* prev_cmd = curr_cmd - 1;\n    if (ImDrawCmd_HeaderCompare(curr_cmd, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && curr_cmd->UserCallback == NULL && prev_cmd->UserCallback == NULL)\n    {\n        prev_cmd->ElemCount += curr_cmd->ElemCount;\n        CmdBuffer.pop_back();\n    }\n}\n\n// Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack.\n// The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only.\nvoid ImDrawList::_OnChangedClipRect()\n{\n    // If current command is used with different settings we need to add a new command\n    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);\n    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    if (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &_CmdHeader.ClipRect, sizeof(ImVec4)) != 0)\n    {\n        AddDrawCmd();\n        return;\n    }\n    IM_ASSERT(curr_cmd->UserCallback == NULL);\n\n    // Try to merge with previous command if it matches, else use current command\n    ImDrawCmd* prev_cmd = curr_cmd - 1;\n    if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL)\n    {\n        CmdBuffer.pop_back();\n        return;\n    }\n\n    curr_cmd->ClipRect = _CmdHeader.ClipRect;\n}\n\nvoid ImDrawList::_OnChangedTextureID()\n{\n    // If current command is used with different settings we need to add a new command\n    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);\n    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    if (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != _CmdHeader.TextureId)\n    {\n        AddDrawCmd();\n        return;\n    }\n    IM_ASSERT(curr_cmd->UserCallback == NULL);\n\n    // Try to merge with previous command if it matches, else use current command\n    ImDrawCmd* prev_cmd = curr_cmd - 1;\n    if (curr_cmd->ElemCount == 0 && CmdBuffer.Size > 1 && ImDrawCmd_HeaderCompare(&_CmdHeader, prev_cmd) == 0 && ImDrawCmd_AreSequentialIdxOffset(prev_cmd, curr_cmd) && prev_cmd->UserCallback == NULL)\n    {\n        CmdBuffer.pop_back();\n        return;\n    }\n\n    curr_cmd->TextureId = _CmdHeader.TextureId;\n}\n\nvoid ImDrawList::_OnChangedVtxOffset()\n{\n    // We don't need to compare curr_cmd->VtxOffset != _CmdHeader.VtxOffset because we know it'll be different at the time we call this.\n    _VtxCurrentIdx = 0;\n    IM_ASSERT_PARANOID(CmdBuffer.Size > 0);\n    ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    //IM_ASSERT(curr_cmd->VtxOffset != _CmdHeader.VtxOffset); // See #3349\n    if (curr_cmd->ElemCount != 0)\n    {\n        AddDrawCmd();\n        return;\n    }\n    IM_ASSERT(curr_cmd->UserCallback == NULL);\n    curr_cmd->VtxOffset = _CmdHeader.VtxOffset;\n}\n\nint ImDrawList::_CalcCircleAutoSegmentCount(float radius) const\n{\n    // Automatic segment count\n    const int radius_idx = (int)(radius + 0.999999f); // ceil to never reduce accuracy\n    if (radius_idx >= 0 && radius_idx < IM_ARRAYSIZE(_Data->CircleSegmentCounts))\n        return _Data->CircleSegmentCounts[radius_idx]; // Use cached value\n    else\n        return IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(radius, _Data->CircleSegmentMaxError);\n}\n\n// Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)\nvoid ImDrawList::PushClipRect(const ImVec2& cr_min, const ImVec2& cr_max, bool intersect_with_current_clip_rect)\n{\n    ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y);\n    if (intersect_with_current_clip_rect)\n    {\n        ImVec4 current = _CmdHeader.ClipRect;\n        if (cr.x < current.x) cr.x = current.x;\n        if (cr.y < current.y) cr.y = current.y;\n        if (cr.z > current.z) cr.z = current.z;\n        if (cr.w > current.w) cr.w = current.w;\n    }\n    cr.z = ImMax(cr.x, cr.z);\n    cr.w = ImMax(cr.y, cr.w);\n\n    _ClipRectStack.push_back(cr);\n    _CmdHeader.ClipRect = cr;\n    _OnChangedClipRect();\n}\n\nvoid ImDrawList::PushClipRectFullScreen()\n{\n    PushClipRect(ImVec2(_Data->ClipRectFullscreen.x, _Data->ClipRectFullscreen.y), ImVec2(_Data->ClipRectFullscreen.z, _Data->ClipRectFullscreen.w));\n}\n\nvoid ImDrawList::PopClipRect()\n{\n    _ClipRectStack.pop_back();\n    _CmdHeader.ClipRect = (_ClipRectStack.Size == 0) ? _Data->ClipRectFullscreen : _ClipRectStack.Data[_ClipRectStack.Size - 1];\n    _OnChangedClipRect();\n}\n\nvoid ImDrawList::PushTextureID(ImTextureID texture_id)\n{\n    _TextureIdStack.push_back(texture_id);\n    _CmdHeader.TextureId = texture_id;\n    _OnChangedTextureID();\n}\n\nvoid ImDrawList::PopTextureID()\n{\n    _TextureIdStack.pop_back();\n    _CmdHeader.TextureId = (_TextureIdStack.Size == 0) ? (ImTextureID)NULL : _TextureIdStack.Data[_TextureIdStack.Size - 1];\n    _OnChangedTextureID();\n}\n\n// Reserve space for a number of vertices and indices.\n// You must finish filling your reserved data before calling PrimReserve() again, as it may reallocate or\n// submit the intermediate results. PrimUnreserve() can be used to release unused allocations.\nvoid ImDrawList::PrimReserve(int idx_count, int vtx_count)\n{\n    // Large mesh support (when enabled)\n    IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);\n    if (sizeof(ImDrawIdx) == 2 && (_VtxCurrentIdx + vtx_count >= (1 << 16)) && (Flags & ImDrawListFlags_AllowVtxOffset))\n    {\n        // FIXME: In theory we should be testing that vtx_count <64k here.\n        // In practice, RenderText() relies on reserving ahead for a worst case scenario so it is currently useful for us\n        // to not make that check until we rework the text functions to handle clipping and large horizontal lines better.\n        _CmdHeader.VtxOffset = VtxBuffer.Size;\n        _OnChangedVtxOffset();\n    }\n\n    ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    draw_cmd->ElemCount += idx_count;\n\n    int vtx_buffer_old_size = VtxBuffer.Size;\n    VtxBuffer.resize(vtx_buffer_old_size + vtx_count);\n    _VtxWritePtr = VtxBuffer.Data + vtx_buffer_old_size;\n\n    int idx_buffer_old_size = IdxBuffer.Size;\n    IdxBuffer.resize(idx_buffer_old_size + idx_count);\n    _IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size;\n}\n\n// Release the a number of reserved vertices/indices from the end of the last reservation made with PrimReserve().\nvoid ImDrawList::PrimUnreserve(int idx_count, int vtx_count)\n{\n    IM_ASSERT_PARANOID(idx_count >= 0 && vtx_count >= 0);\n\n    ImDrawCmd* draw_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1];\n    draw_cmd->ElemCount -= idx_count;\n    VtxBuffer.shrink(VtxBuffer.Size - vtx_count);\n    IdxBuffer.shrink(IdxBuffer.Size - idx_count);\n}\n\n// Fully unrolled with inline call to keep our debug builds decently fast.\nvoid ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col)\n{\n    ImVec2 b(c.x, a.y), d(a.x, c.y), uv(_Data->TexUvWhitePixel);\n    ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;\n    _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);\n    _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);\n    _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;\n    _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col;\n    _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col;\n    _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col;\n    _VtxWritePtr += 4;\n    _VtxCurrentIdx += 4;\n    _IdxWritePtr += 6;\n}\n\nvoid ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col)\n{\n    ImVec2 b(c.x, a.y), d(a.x, c.y), uv_b(uv_c.x, uv_a.y), uv_d(uv_a.x, uv_c.y);\n    ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;\n    _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);\n    _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);\n    _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col;\n    _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col;\n    _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col;\n    _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col;\n    _VtxWritePtr += 4;\n    _VtxCurrentIdx += 4;\n    _IdxWritePtr += 6;\n}\n\nvoid ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col)\n{\n    ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;\n    _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);\n    _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);\n    _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col;\n    _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col;\n    _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col;\n    _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col;\n    _VtxWritePtr += 4;\n    _VtxCurrentIdx += 4;\n    _IdxWritePtr += 6;\n}\n\n// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superfluous function calls to optimize debug/non-inlined builds.\n// - Those macros expects l-values and need to be used as their own statement.\n// - Those macros are intentionally not surrounded by the 'do {} while (0)' idiom because even that translates to runtime with debug compilers.\n#define IM_NORMALIZE2F_OVER_ZERO(VX,VY)     { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = ImRsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0\n#define IM_FIXNORMAL2F_MAX_INVLEN2          100.0f // 500.0f (see #4053, #3366)\n#define IM_FIXNORMAL2F(VX,VY)               { float d2 = VX*VX + VY*VY; if (d2 > 0.000001f) { float inv_len2 = 1.0f / d2; if (inv_len2 > IM_FIXNORMAL2F_MAX_INVLEN2) inv_len2 = IM_FIXNORMAL2F_MAX_INVLEN2; VX *= inv_len2; VY *= inv_len2; } } (void)0\n\n// TODO: Thickness anti-aliased lines cap are missing their AA fringe.\n// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.\nvoid ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, ImDrawFlags flags, float thickness)\n{\n    if (points_count < 2 || (col & IM_COL32_A_MASK) == 0)\n        return;\n\n    const bool closed = (flags & ImDrawFlags_Closed) != 0;\n    const ImVec2 opaque_uv = _Data->TexUvWhitePixel;\n    const int count = closed ? points_count : points_count - 1; // The number of line segments we need to draw\n    const bool thick_line = (thickness > _FringeScale);\n\n    if (Flags & ImDrawListFlags_AntiAliasedLines)\n    {\n        // Anti-aliased stroke\n        const float AA_SIZE = _FringeScale;\n        const ImU32 col_trans = col & ~IM_COL32_A_MASK;\n\n        // Thicknesses <1.0 should behave like thickness 1.0\n        thickness = ImMax(thickness, 1.0f);\n        const int integer_thickness = (int)thickness;\n        const float fractional_thickness = thickness - integer_thickness;\n\n        // Do we want to draw this line using a texture?\n        // - For now, only draw integer-width lines using textures to avoid issues with the way scaling occurs, could be improved.\n        // - If AA_SIZE is not 1.0f we cannot use the texture path.\n        const bool use_texture = (Flags & ImDrawListFlags_AntiAliasedLinesUseTex) && (integer_thickness < IM_DRAWLIST_TEX_LINES_WIDTH_MAX) && (fractional_thickness <= 0.00001f) && (AA_SIZE == 1.0f);\n\n        // We should never hit this, because NewFrame() doesn't set ImDrawListFlags_AntiAliasedLinesUseTex unless ImFontAtlasFlags_NoBakedLines is off\n        IM_ASSERT_PARANOID(!use_texture || !(_Data->Font->ContainerAtlas->Flags & ImFontAtlasFlags_NoBakedLines));\n\n        const int idx_count = use_texture ? (count * 6) : (thick_line ? count * 18 : count * 12);\n        const int vtx_count = use_texture ? (points_count * 2) : (thick_line ? points_count * 4 : points_count * 3);\n        PrimReserve(idx_count, vtx_count);\n\n        // Temporary buffer\n        // The first <points_count> items are normals at each line point, then after that there are either 2 or 4 temp points for each line point\n        _Data->TempBuffer.reserve_discard(points_count * ((use_texture || !thick_line) ? 3 : 5));\n        ImVec2* temp_normals = _Data->TempBuffer.Data;\n        ImVec2* temp_points = temp_normals + points_count;\n\n        // Calculate normals (tangents) for each line segment\n        for (int i1 = 0; i1 < count; i1++)\n        {\n            const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1;\n            float dx = points[i2].x - points[i1].x;\n            float dy = points[i2].y - points[i1].y;\n            IM_NORMALIZE2F_OVER_ZERO(dx, dy);\n            temp_normals[i1].x = dy;\n            temp_normals[i1].y = -dx;\n        }\n        if (!closed)\n            temp_normals[points_count - 1] = temp_normals[points_count - 2];\n\n        // If we are drawing a one-pixel-wide line without a texture, or a textured line of any width, we only need 2 or 3 vertices per point\n        if (use_texture || !thick_line)\n        {\n            // [PATH 1] Texture-based lines (thick or non-thick)\n            // [PATH 2] Non texture-based lines (non-thick)\n\n            // The width of the geometry we need to draw - this is essentially <thickness> pixels for the line itself, plus \"one pixel\" for AA.\n            // - In the texture-based path, we don't use AA_SIZE here because the +1 is tied to the generated texture\n            //   (see ImFontAtlasBuildRenderLinesTexData() function), and so alternate values won't work without changes to that code.\n            // - In the non texture-based paths, we would allow AA_SIZE to potentially be != 1.0f with a patch (e.g. fringe_scale patch to\n            //   allow scaling geometry while preserving one-screen-pixel AA fringe).\n            const float half_draw_size = use_texture ? ((thickness * 0.5f) + 1) : AA_SIZE;\n\n            // If line is not closed, the first and last points need to be generated differently as there are no normals to blend\n            if (!closed)\n            {\n                temp_points[0] = points[0] + temp_normals[0] * half_draw_size;\n                temp_points[1] = points[0] - temp_normals[0] * half_draw_size;\n                temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * half_draw_size;\n                temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * half_draw_size;\n            }\n\n            // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges\n            // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps)\n            // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer.\n            unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment\n            for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment\n            {\n                const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1; // i2 is the second point of the line segment\n                const unsigned int idx2 = ((i1 + 1) == points_count) ? _VtxCurrentIdx : (idx1 + (use_texture ? 2 : 3)); // Vertex index for end of segment\n\n                // Average normals\n                float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;\n                float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;\n                IM_FIXNORMAL2F(dm_x, dm_y);\n                dm_x *= half_draw_size; // dm_x, dm_y are offset to the outer edge of the AA area\n                dm_y *= half_draw_size;\n\n                // Add temporary vertexes for the outer edges\n                ImVec2* out_vtx = &temp_points[i2 * 2];\n                out_vtx[0].x = points[i2].x + dm_x;\n                out_vtx[0].y = points[i2].y + dm_y;\n                out_vtx[1].x = points[i2].x - dm_x;\n                out_vtx[1].y = points[i2].y - dm_y;\n\n                if (use_texture)\n                {\n                    // Add indices for two triangles\n                    _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 1); // Right tri\n                    _IdxWritePtr[3] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[4] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Left tri\n                    _IdxWritePtr += 6;\n                }\n                else\n                {\n                    // Add indexes for four triangles\n                    _IdxWritePtr[0] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[1] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[2] = (ImDrawIdx)(idx1 + 2); // Right tri 1\n                    _IdxWritePtr[3] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5] = (ImDrawIdx)(idx2 + 0); // Right tri 2\n                    _IdxWritePtr[6] = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7] = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8] = (ImDrawIdx)(idx1 + 0); // Left tri 1\n                    _IdxWritePtr[9] = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1); // Left tri 2\n                    _IdxWritePtr += 12;\n                }\n\n                idx1 = idx2;\n            }\n\n            // Add vertexes for each point on the line\n            if (use_texture)\n            {\n                // If we're using textures we only need to emit the left/right edge vertices\n                ImVec4 tex_uvs = _Data->TexUvLines[integer_thickness];\n                /*if (fractional_thickness != 0.0f) // Currently always zero when use_texture==false!\n                {\n                    const ImVec4 tex_uvs_1 = _Data->TexUvLines[integer_thickness + 1];\n                    tex_uvs.x = tex_uvs.x + (tex_uvs_1.x - tex_uvs.x) * fractional_thickness; // inlined ImLerp()\n                    tex_uvs.y = tex_uvs.y + (tex_uvs_1.y - tex_uvs.y) * fractional_thickness;\n                    tex_uvs.z = tex_uvs.z + (tex_uvs_1.z - tex_uvs.z) * fractional_thickness;\n                    tex_uvs.w = tex_uvs.w + (tex_uvs_1.w - tex_uvs.w) * fractional_thickness;\n                }*/\n                ImVec2 tex_uv0(tex_uvs.x, tex_uvs.y);\n                ImVec2 tex_uv1(tex_uvs.z, tex_uvs.w);\n                for (int i = 0; i < points_count; i++)\n                {\n                    _VtxWritePtr[0].pos = temp_points[i * 2 + 0]; _VtxWritePtr[0].uv = tex_uv0; _VtxWritePtr[0].col = col; // Left-side outer edge\n                    _VtxWritePtr[1].pos = temp_points[i * 2 + 1]; _VtxWritePtr[1].uv = tex_uv1; _VtxWritePtr[1].col = col; // Right-side outer edge\n                    _VtxWritePtr += 2;\n                }\n            }\n            else\n            {\n                // If we're not using a texture, we need the center vertex as well\n                for (int i = 0; i < points_count; i++)\n                {\n                    _VtxWritePtr[0].pos = points[i];              _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col;       // Center of line\n                    _VtxWritePtr[1].pos = temp_points[i * 2 + 0]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col_trans; // Left-side outer edge\n                    _VtxWritePtr[2].pos = temp_points[i * 2 + 1]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col_trans; // Right-side outer edge\n                    _VtxWritePtr += 3;\n                }\n            }\n        }\n        else\n        {\n            // [PATH 2] Non texture-based lines (thick): we need to draw the solid line core and thus require four vertices per point\n            const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f;\n\n            // If line is not closed, the first and last points need to be generated differently as there are no normals to blend\n            if (!closed)\n            {\n                const int points_last = points_count - 1;\n                temp_points[0] = points[0] + temp_normals[0] * (half_inner_thickness + AA_SIZE);\n                temp_points[1] = points[0] + temp_normals[0] * (half_inner_thickness);\n                temp_points[2] = points[0] - temp_normals[0] * (half_inner_thickness);\n                temp_points[3] = points[0] - temp_normals[0] * (half_inner_thickness + AA_SIZE);\n                temp_points[points_last * 4 + 0] = points[points_last] + temp_normals[points_last] * (half_inner_thickness + AA_SIZE);\n                temp_points[points_last * 4 + 1] = points[points_last] + temp_normals[points_last] * (half_inner_thickness);\n                temp_points[points_last * 4 + 2] = points[points_last] - temp_normals[points_last] * (half_inner_thickness);\n                temp_points[points_last * 4 + 3] = points[points_last] - temp_normals[points_last] * (half_inner_thickness + AA_SIZE);\n            }\n\n            // Generate the indices to form a number of triangles for each line segment, and the vertices for the line edges\n            // This takes points n and n+1 and writes into n+1, with the first point in a closed line being generated from the final one (as n+1 wraps)\n            // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer.\n            unsigned int idx1 = _VtxCurrentIdx; // Vertex index for start of line segment\n            for (int i1 = 0; i1 < count; i1++) // i1 is the first point of the line segment\n            {\n                const int i2 = (i1 + 1) == points_count ? 0 : (i1 + 1); // i2 is the second point of the line segment\n                const unsigned int idx2 = (i1 + 1) == points_count ? _VtxCurrentIdx : (idx1 + 4); // Vertex index for end of segment\n\n                // Average normals\n                float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;\n                float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;\n                IM_FIXNORMAL2F(dm_x, dm_y);\n                float dm_out_x = dm_x * (half_inner_thickness + AA_SIZE);\n                float dm_out_y = dm_y * (half_inner_thickness + AA_SIZE);\n                float dm_in_x = dm_x * half_inner_thickness;\n                float dm_in_y = dm_y * half_inner_thickness;\n\n                // Add temporary vertices\n                ImVec2* out_vtx = &temp_points[i2 * 4];\n                out_vtx[0].x = points[i2].x + dm_out_x;\n                out_vtx[0].y = points[i2].y + dm_out_y;\n                out_vtx[1].x = points[i2].x + dm_in_x;\n                out_vtx[1].y = points[i2].y + dm_in_y;\n                out_vtx[2].x = points[i2].x - dm_in_x;\n                out_vtx[2].y = points[i2].y - dm_in_y;\n                out_vtx[3].x = points[i2].x - dm_out_x;\n                out_vtx[3].y = points[i2].y - dm_out_y;\n\n                // Add indexes\n                _IdxWritePtr[0]  = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[1]  = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[2]  = (ImDrawIdx)(idx1 + 2);\n                _IdxWritePtr[3]  = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[4]  = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[5]  = (ImDrawIdx)(idx2 + 1);\n                _IdxWritePtr[6]  = (ImDrawIdx)(idx2 + 1); _IdxWritePtr[7]  = (ImDrawIdx)(idx1 + 1); _IdxWritePtr[8]  = (ImDrawIdx)(idx1 + 0);\n                _IdxWritePtr[9]  = (ImDrawIdx)(idx1 + 0); _IdxWritePtr[10] = (ImDrawIdx)(idx2 + 0); _IdxWritePtr[11] = (ImDrawIdx)(idx2 + 1);\n                _IdxWritePtr[12] = (ImDrawIdx)(idx2 + 2); _IdxWritePtr[13] = (ImDrawIdx)(idx1 + 2); _IdxWritePtr[14] = (ImDrawIdx)(idx1 + 3);\n                _IdxWritePtr[15] = (ImDrawIdx)(idx1 + 3); _IdxWritePtr[16] = (ImDrawIdx)(idx2 + 3); _IdxWritePtr[17] = (ImDrawIdx)(idx2 + 2);\n                _IdxWritePtr += 18;\n\n                idx1 = idx2;\n            }\n\n            // Add vertices\n            for (int i = 0; i < points_count; i++)\n            {\n                _VtxWritePtr[0].pos = temp_points[i * 4 + 0]; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col_trans;\n                _VtxWritePtr[1].pos = temp_points[i * 4 + 1]; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col;\n                _VtxWritePtr[2].pos = temp_points[i * 4 + 2]; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col;\n                _VtxWritePtr[3].pos = temp_points[i * 4 + 3]; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col_trans;\n                _VtxWritePtr += 4;\n            }\n        }\n        _VtxCurrentIdx += (ImDrawIdx)vtx_count;\n    }\n    else\n    {\n        // [PATH 4] Non texture-based, Non anti-aliased lines\n        const int idx_count = count * 6;\n        const int vtx_count = count * 4;    // FIXME-OPT: Not sharing edges\n        PrimReserve(idx_count, vtx_count);\n\n        for (int i1 = 0; i1 < count; i1++)\n        {\n            const int i2 = (i1 + 1) == points_count ? 0 : i1 + 1;\n            const ImVec2& p1 = points[i1];\n            const ImVec2& p2 = points[i2];\n\n            float dx = p2.x - p1.x;\n            float dy = p2.y - p1.y;\n            IM_NORMALIZE2F_OVER_ZERO(dx, dy);\n            dx *= (thickness * 0.5f);\n            dy *= (thickness * 0.5f);\n\n            _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = opaque_uv; _VtxWritePtr[0].col = col;\n            _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = opaque_uv; _VtxWritePtr[1].col = col;\n            _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = opaque_uv; _VtxWritePtr[2].col = col;\n            _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = opaque_uv; _VtxWritePtr[3].col = col;\n            _VtxWritePtr += 4;\n\n            _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + 2);\n            _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx + 2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx + 3);\n            _IdxWritePtr += 6;\n            _VtxCurrentIdx += 4;\n        }\n    }\n}\n\n// - We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds.\n// - Filled shapes must always use clockwise winding order. The anti-aliasing fringe depends on it. Counter-clockwise shapes will have \"inward\" anti-aliasing.\nvoid ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col)\n{\n    if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)\n        return;\n\n    const ImVec2 uv = _Data->TexUvWhitePixel;\n\n    if (Flags & ImDrawListFlags_AntiAliasedFill)\n    {\n        // Anti-aliased Fill\n        const float AA_SIZE = _FringeScale;\n        const ImU32 col_trans = col & ~IM_COL32_A_MASK;\n        const int idx_count = (points_count - 2)*3 + points_count * 6;\n        const int vtx_count = (points_count * 2);\n        PrimReserve(idx_count, vtx_count);\n\n        // Add indexes for fill\n        unsigned int vtx_inner_idx = _VtxCurrentIdx;\n        unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;\n        for (int i = 2; i < points_count; i++)\n        {\n            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + ((i - 1) << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (i << 1));\n            _IdxWritePtr += 3;\n        }\n\n        // Compute normals\n        _Data->TempBuffer.reserve_discard(points_count);\n        ImVec2* temp_normals = _Data->TempBuffer.Data;\n        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)\n        {\n            const ImVec2& p0 = points[i0];\n            const ImVec2& p1 = points[i1];\n            float dx = p1.x - p0.x;\n            float dy = p1.y - p0.y;\n            IM_NORMALIZE2F_OVER_ZERO(dx, dy);\n            temp_normals[i0].x = dy;\n            temp_normals[i0].y = -dx;\n        }\n\n        for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)\n        {\n            // Average normals\n            const ImVec2& n0 = temp_normals[i0];\n            const ImVec2& n1 = temp_normals[i1];\n            float dm_x = (n0.x + n1.x) * 0.5f;\n            float dm_y = (n0.y + n1.y) * 0.5f;\n            IM_FIXNORMAL2F(dm_x, dm_y);\n            dm_x *= AA_SIZE * 0.5f;\n            dm_y *= AA_SIZE * 0.5f;\n\n            // Add vertices\n            _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;        // Inner\n            _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans;  // Outer\n            _VtxWritePtr += 2;\n\n            // Add indexes for fringes\n            _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));\n            _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));\n            _IdxWritePtr += 6;\n        }\n        _VtxCurrentIdx += (ImDrawIdx)vtx_count;\n    }\n    else\n    {\n        // Non Anti-aliased Fill\n        const int idx_count = (points_count - 2)*3;\n        const int vtx_count = points_count;\n        PrimReserve(idx_count, vtx_count);\n        for (int i = 0; i < vtx_count; i++)\n        {\n            _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;\n            _VtxWritePtr++;\n        }\n        for (int i = 2; i < points_count; i++)\n        {\n            _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + i - 1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + i);\n            _IdxWritePtr += 3;\n        }\n        _VtxCurrentIdx += (ImDrawIdx)vtx_count;\n    }\n}\n\nvoid ImDrawList::_PathArcToFastEx(const ImVec2& center, float radius, int a_min_sample, int a_max_sample, int a_step)\n{\n    if (radius < 0.5f)\n    {\n        _Path.push_back(center);\n        return;\n    }\n\n    // Calculate arc auto segment step size\n    if (a_step <= 0)\n        a_step = IM_DRAWLIST_ARCFAST_SAMPLE_MAX / _CalcCircleAutoSegmentCount(radius);\n\n    // Make sure we never do steps larger than one quarter of the circle\n    a_step = ImClamp(a_step, 1, IM_DRAWLIST_ARCFAST_TABLE_SIZE / 4);\n\n    const int sample_range = ImAbs(a_max_sample - a_min_sample);\n    const int a_next_step = a_step;\n\n    int samples = sample_range + 1;\n    bool extra_max_sample = false;\n    if (a_step > 1)\n    {\n        samples            = sample_range / a_step + 1;\n        const int overstep = sample_range % a_step;\n\n        if (overstep > 0)\n        {\n            extra_max_sample = true;\n            samples++;\n\n            // When we have overstep to avoid awkwardly looking one long line and one tiny one at the end,\n            // distribute first step range evenly between them by reducing first step size.\n            if (sample_range > 0)\n                a_step -= (a_step - overstep) / 2;\n        }\n    }\n\n    _Path.resize(_Path.Size + samples);\n    ImVec2* out_ptr = _Path.Data + (_Path.Size - samples);\n\n    int sample_index = a_min_sample;\n    if (sample_index < 0 || sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)\n    {\n        sample_index = sample_index % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n        if (sample_index < 0)\n            sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n    }\n\n    if (a_max_sample >= a_min_sample)\n    {\n        for (int a = a_min_sample; a <= a_max_sample; a += a_step, sample_index += a_step, a_step = a_next_step)\n        {\n            // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more\n            if (sample_index >= IM_DRAWLIST_ARCFAST_SAMPLE_MAX)\n                sample_index -= IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n\n            const ImVec2 s = _Data->ArcFastVtx[sample_index];\n            out_ptr->x = center.x + s.x * radius;\n            out_ptr->y = center.y + s.y * radius;\n            out_ptr++;\n        }\n    }\n    else\n    {\n        for (int a = a_min_sample; a >= a_max_sample; a -= a_step, sample_index -= a_step, a_step = a_next_step)\n        {\n            // a_step is clamped to IM_DRAWLIST_ARCFAST_SAMPLE_MAX, so we have guaranteed that it will not wrap over range twice or more\n            if (sample_index < 0)\n                sample_index += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n\n            const ImVec2 s = _Data->ArcFastVtx[sample_index];\n            out_ptr->x = center.x + s.x * radius;\n            out_ptr->y = center.y + s.y * radius;\n            out_ptr++;\n        }\n    }\n\n    if (extra_max_sample)\n    {\n        int normalized_max_sample = a_max_sample % IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n        if (normalized_max_sample < 0)\n            normalized_max_sample += IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n\n        const ImVec2 s = _Data->ArcFastVtx[normalized_max_sample];\n        out_ptr->x = center.x + s.x * radius;\n        out_ptr->y = center.y + s.y * radius;\n        out_ptr++;\n    }\n\n    IM_ASSERT_PARANOID(_Path.Data + _Path.Size == out_ptr);\n}\n\nvoid ImDrawList::_PathArcToN(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)\n{\n    if (radius < 0.5f)\n    {\n        _Path.push_back(center);\n        return;\n    }\n\n    // Note that we are adding a point at both a_min and a_max.\n    // If you are trying to draw a full closed circle you don't want the overlapping points!\n    _Path.reserve(_Path.Size + (num_segments + 1));\n    for (int i = 0; i <= num_segments; i++)\n    {\n        const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);\n        _Path.push_back(ImVec2(center.x + ImCos(a) * radius, center.y + ImSin(a) * radius));\n    }\n}\n\n// 0: East, 3: South, 6: West, 9: North, 12: East\nvoid ImDrawList::PathArcToFast(const ImVec2& center, float radius, int a_min_of_12, int a_max_of_12)\n{\n    if (radius < 0.5f)\n    {\n        _Path.push_back(center);\n        return;\n    }\n    _PathArcToFastEx(center, radius, a_min_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, a_max_of_12 * IM_DRAWLIST_ARCFAST_SAMPLE_MAX / 12, 0);\n}\n\nvoid ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, float a_max, int num_segments)\n{\n    if (radius < 0.5f)\n    {\n        _Path.push_back(center);\n        return;\n    }\n\n    if (num_segments > 0)\n    {\n        _PathArcToN(center, radius, a_min, a_max, num_segments);\n        return;\n    }\n\n    // Automatic segment count\n    if (radius <= _Data->ArcFastRadiusCutoff)\n    {\n        const bool a_is_reverse = a_max < a_min;\n\n        // We are going to use precomputed values for mid samples.\n        // Determine first and last sample in lookup table that belong to the arc.\n        const float a_min_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_min / (IM_PI * 2.0f);\n        const float a_max_sample_f = IM_DRAWLIST_ARCFAST_SAMPLE_MAX * a_max / (IM_PI * 2.0f);\n\n        const int a_min_sample = a_is_reverse ? (int)ImFloor(a_min_sample_f) : (int)ImCeil(a_min_sample_f);\n        const int a_max_sample = a_is_reverse ? (int)ImCeil(a_max_sample_f) : (int)ImFloor(a_max_sample_f);\n        const int a_mid_samples = a_is_reverse ? ImMax(a_min_sample - a_max_sample, 0) : ImMax(a_max_sample - a_min_sample, 0);\n\n        const float a_min_segment_angle = a_min_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n        const float a_max_segment_angle = a_max_sample * IM_PI * 2.0f / IM_DRAWLIST_ARCFAST_SAMPLE_MAX;\n        const bool a_emit_start = ImAbs(a_min_segment_angle - a_min) >= 1e-5f;\n        const bool a_emit_end = ImAbs(a_max - a_max_segment_angle) >= 1e-5f;\n\n        _Path.reserve(_Path.Size + (a_mid_samples + 1 + (a_emit_start ? 1 : 0) + (a_emit_end ? 1 : 0)));\n        if (a_emit_start)\n            _Path.push_back(ImVec2(center.x + ImCos(a_min) * radius, center.y + ImSin(a_min) * radius));\n        if (a_mid_samples > 0)\n            _PathArcToFastEx(center, radius, a_min_sample, a_max_sample, 0);\n        if (a_emit_end)\n            _Path.push_back(ImVec2(center.x + ImCos(a_max) * radius, center.y + ImSin(a_max) * radius));\n    }\n    else\n    {\n        const float arc_length = ImAbs(a_max - a_min);\n        const int circle_segment_count = _CalcCircleAutoSegmentCount(radius);\n        const int arc_segment_count = ImMax((int)ImCeil(circle_segment_count * arc_length / (IM_PI * 2.0f)), (int)(2.0f * IM_PI / arc_length));\n        _PathArcToN(center, radius, a_min, a_max, arc_segment_count);\n    }\n}\n\nvoid ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments)\n{\n    if (num_segments <= 0)\n        num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.\n\n    _Path.reserve(_Path.Size + (num_segments + 1));\n\n    const float cos_rot = ImCos(rot);\n    const float sin_rot = ImSin(rot);\n    for (int i = 0; i <= num_segments; i++)\n    {\n        const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);\n        ImVec2 point(ImCos(a) * radius_x, ImSin(a) * radius_y);\n        const float rel_x = (point.x * cos_rot) - (point.y * sin_rot);\n        const float rel_y = (point.x * sin_rot) + (point.y * cos_rot);\n        point.x = rel_x + center.x;\n        point.y = rel_y + center.y;\n        _Path.push_back(point);\n    }\n}\n\nImVec2 ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t)\n{\n    float u = 1.0f - t;\n    float w1 = u * u * u;\n    float w2 = 3 * u * u * t;\n    float w3 = 3 * u * t * t;\n    float w4 = t * t * t;\n    return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x, w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y);\n}\n\nImVec2 ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t)\n{\n    float u = 1.0f - t;\n    float w1 = u * u;\n    float w2 = 2 * u * t;\n    float w3 = t * t;\n    return ImVec2(w1 * p1.x + w2 * p2.x + w3 * p3.x, w1 * p1.y + w2 * p2.y + w3 * p3.y);\n}\n\n// Closely mimics ImBezierCubicClosestPointCasteljau() in imgui.cpp\nstatic void PathBezierCubicCurveToCasteljau(ImVector<ImVec2>* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)\n{\n    float dx = x4 - x1;\n    float dy = y4 - y1;\n    float d2 = (x2 - x4) * dy - (y2 - y4) * dx;\n    float d3 = (x3 - x4) * dy - (y3 - y4) * dx;\n    d2 = (d2 >= 0) ? d2 : -d2;\n    d3 = (d3 >= 0) ? d3 : -d3;\n    if ((d2 + d3) * (d2 + d3) < tess_tol * (dx * dx + dy * dy))\n    {\n        path->push_back(ImVec2(x4, y4));\n    }\n    else if (level < 10)\n    {\n        float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f;\n        float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f;\n        float x34 = (x3 + x4) * 0.5f, y34 = (y3 + y4) * 0.5f;\n        float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f;\n        float x234 = (x23 + x34) * 0.5f, y234 = (y23 + y34) * 0.5f;\n        float x1234 = (x123 + x234) * 0.5f, y1234 = (y123 + y234) * 0.5f;\n        PathBezierCubicCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, x1234, y1234, tess_tol, level + 1);\n        PathBezierCubicCurveToCasteljau(path, x1234, y1234, x234, y234, x34, y34, x4, y4, tess_tol, level + 1);\n    }\n}\n\nstatic void PathBezierQuadraticCurveToCasteljau(ImVector<ImVec2>* path, float x1, float y1, float x2, float y2, float x3, float y3, float tess_tol, int level)\n{\n    float dx = x3 - x1, dy = y3 - y1;\n    float det = (x2 - x3) * dy - (y2 - y3) * dx;\n    if (det * det * 4.0f < tess_tol * (dx * dx + dy * dy))\n    {\n        path->push_back(ImVec2(x3, y3));\n    }\n    else if (level < 10)\n    {\n        float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f;\n        float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f;\n        float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f;\n        PathBezierQuadraticCurveToCasteljau(path, x1, y1, x12, y12, x123, y123, tess_tol, level + 1);\n        PathBezierQuadraticCurveToCasteljau(path, x123, y123, x23, y23, x3, y3, tess_tol, level + 1);\n    }\n}\n\nvoid ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments)\n{\n    ImVec2 p1 = _Path.back();\n    if (num_segments == 0)\n    {\n        IM_ASSERT(_Data->CurveTessellationTol > 0.0f);\n        PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated\n    }\n    else\n    {\n        float t_step = 1.0f / (float)num_segments;\n        for (int i_step = 1; i_step <= num_segments; i_step++)\n            _Path.push_back(ImBezierCubicCalc(p1, p2, p3, p4, t_step * i_step));\n    }\n}\n\nvoid ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, int num_segments)\n{\n    ImVec2 p1 = _Path.back();\n    if (num_segments == 0)\n    {\n        IM_ASSERT(_Data->CurveTessellationTol > 0.0f);\n        PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated\n    }\n    else\n    {\n        float t_step = 1.0f / (float)num_segments;\n        for (int i_step = 1; i_step <= num_segments; i_step++)\n            _Path.push_back(ImBezierQuadraticCalc(p1, p2, p3, t_step * i_step));\n    }\n}\n\nstatic inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags)\n{\n    /*\n    IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4));\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    // Obsoleted in 1.82 (from February 2021). This code was stripped/simplified and mostly commented in 1.90 (from September 2023)\n    // - Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All)\n    if (flags == ~0)                    { return ImDrawFlags_RoundCornersAll; }\n    // - Legacy Support for hard coded 0x01 to 0x0F (matching 15 out of 16 old flags combinations). Read details in older version of this code.\n    if (flags >= 0x01 && flags <= 0x0F) { return (flags << 4); }\n    // We cannot support hard coded 0x00 with 'float rounding > 0.0f' --> replace with ImDrawFlags_RoundCornersNone or use 'float rounding = 0.0f'\n#endif\n    */\n    // If this assert triggers, please update your code replacing hardcoded values with new ImDrawFlags_RoundCorners* values.\n    // Note that ImDrawFlags_Closed (== 0x01) is an invalid flag for AddRect(), AddRectFilled(), PathRect() etc. anyway.\n    // See details in 1.82 Changelog as well as 2021/03/12 and 2023/09/08 entries in \"API BREAKING CHANGES\" section.\n    IM_ASSERT((flags & 0x0F) == 0 && \"Misuse of legacy hardcoded ImDrawCornerFlags values!\");\n\n    if ((flags & ImDrawFlags_RoundCornersMask_) == 0)\n        flags |= ImDrawFlags_RoundCornersAll;\n\n    return flags;\n}\n\nvoid ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, ImDrawFlags flags)\n{\n    if (rounding >= 0.5f)\n    {\n        flags = FixRectCornerFlags(flags);\n        rounding = ImMin(rounding, ImFabs(b.x - a.x) * (((flags & ImDrawFlags_RoundCornersTop) == ImDrawFlags_RoundCornersTop) || ((flags & ImDrawFlags_RoundCornersBottom) == ImDrawFlags_RoundCornersBottom) ? 0.5f : 1.0f) - 1.0f);\n        rounding = ImMin(rounding, ImFabs(b.y - a.y) * (((flags & ImDrawFlags_RoundCornersLeft) == ImDrawFlags_RoundCornersLeft) || ((flags & ImDrawFlags_RoundCornersRight) == ImDrawFlags_RoundCornersRight) ? 0.5f : 1.0f) - 1.0f);\n    }\n    if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)\n    {\n        PathLineTo(a);\n        PathLineTo(ImVec2(b.x, a.y));\n        PathLineTo(b);\n        PathLineTo(ImVec2(a.x, b.y));\n    }\n    else\n    {\n        const float rounding_tl = (flags & ImDrawFlags_RoundCornersTopLeft)     ? rounding : 0.0f;\n        const float rounding_tr = (flags & ImDrawFlags_RoundCornersTopRight)    ? rounding : 0.0f;\n        const float rounding_br = (flags & ImDrawFlags_RoundCornersBottomRight) ? rounding : 0.0f;\n        const float rounding_bl = (flags & ImDrawFlags_RoundCornersBottomLeft)  ? rounding : 0.0f;\n        PathArcToFast(ImVec2(a.x + rounding_tl, a.y + rounding_tl), rounding_tl, 6, 9);\n        PathArcToFast(ImVec2(b.x - rounding_tr, a.y + rounding_tr), rounding_tr, 9, 12);\n        PathArcToFast(ImVec2(b.x - rounding_br, b.y - rounding_br), rounding_br, 0, 3);\n        PathArcToFast(ImVec2(a.x + rounding_bl, b.y - rounding_bl), rounding_bl, 3, 6);\n    }\n}\n\nvoid ImDrawList::AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n    PathLineTo(p1 + ImVec2(0.5f, 0.5f));\n    PathLineTo(p2 + ImVec2(0.5f, 0.5f));\n    PathStroke(col, 0, thickness);\n}\n\n// p_min = upper-left, p_max = lower-right\n// Note we don't render 1 pixels sized rectangles properly.\nvoid ImDrawList::AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n    if (Flags & ImDrawListFlags_AntiAliasedLines)\n        PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.50f, 0.50f), rounding, flags);\n    else\n        PathRect(p_min + ImVec2(0.50f, 0.50f), p_max - ImVec2(0.49f, 0.49f), rounding, flags); // Better looking lower-right corner and rounded non-AA shapes.\n    PathStroke(col, ImDrawFlags_Closed, thickness);\n}\n\nvoid ImDrawList::AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding, ImDrawFlags flags)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n    if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)\n    {\n        PrimReserve(6, 4);\n        PrimRect(p_min, p_max, col);\n    }\n    else\n    {\n        PathRect(p_min, p_max, rounding, flags);\n        PathFillConvex(col);\n    }\n}\n\n// p_min = upper-left, p_max = lower-right\nvoid ImDrawList::AddRectFilledMultiColor(const ImVec2& p_min, const ImVec2& p_max, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left)\n{\n    if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) & IM_COL32_A_MASK) == 0)\n        return;\n\n    const ImVec2 uv = _Data->TexUvWhitePixel;\n    PrimReserve(6, 4);\n    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2));\n    PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx + 3));\n    PrimWriteVtx(p_min, uv, col_upr_left);\n    PrimWriteVtx(ImVec2(p_max.x, p_min.y), uv, col_upr_right);\n    PrimWriteVtx(p_max, uv, col_bot_right);\n    PrimWriteVtx(ImVec2(p_min.x, p_max.y), uv, col_bot_left);\n}\n\nvoid ImDrawList::AddQuad(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathLineTo(p2);\n    PathLineTo(p3);\n    PathLineTo(p4);\n    PathStroke(col, ImDrawFlags_Closed, thickness);\n}\n\nvoid ImDrawList::AddQuadFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathLineTo(p2);\n    PathLineTo(p3);\n    PathLineTo(p4);\n    PathFillConvex(col);\n}\n\nvoid ImDrawList::AddTriangle(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathLineTo(p2);\n    PathLineTo(p3);\n    PathStroke(col, ImDrawFlags_Closed, thickness);\n}\n\nvoid ImDrawList::AddTriangleFilled(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathLineTo(p2);\n    PathLineTo(p3);\n    PathFillConvex(col);\n}\n\nvoid ImDrawList::AddCircle(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f)\n        return;\n\n    if (num_segments <= 0)\n    {\n        // Use arc with automatic segment count\n        _PathArcToFastEx(center, radius - 0.5f, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0);\n        _Path.Size--;\n    }\n    else\n    {\n        // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)\n        num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX);\n\n        // Because we are filling a closed shape we remove 1 from the count of segments/points\n        const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;\n        PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);\n    }\n\n    PathStroke(col, ImDrawFlags_Closed, thickness);\n}\n\nvoid ImDrawList::AddCircleFilled(const ImVec2& center, float radius, ImU32 col, int num_segments)\n{\n    if ((col & IM_COL32_A_MASK) == 0 || radius < 0.5f)\n        return;\n\n    if (num_segments <= 0)\n    {\n        // Use arc with automatic segment count\n        _PathArcToFastEx(center, radius, 0, IM_DRAWLIST_ARCFAST_SAMPLE_MAX, 0);\n        _Path.Size--;\n    }\n    else\n    {\n        // Explicit segment count (still clamp to avoid drawing insanely tessellated shapes)\n        num_segments = ImClamp(num_segments, 3, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX);\n\n        // Because we are filling a closed shape we remove 1 from the count of segments/points\n        const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;\n        PathArcTo(center, radius, 0.0f, a_max, num_segments - 1);\n    }\n\n    PathFillConvex(col);\n}\n\n// Guaranteed to honor 'num_segments'\nvoid ImDrawList::AddNgon(const ImVec2& center, float radius, ImU32 col, int num_segments, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2)\n        return;\n\n    // Because we are filling a closed shape we remove 1 from the count of segments/points\n    const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;\n    PathArcTo(center, radius - 0.5f, 0.0f, a_max, num_segments - 1);\n    PathStroke(col, ImDrawFlags_Closed, thickness);\n}\n\n// Guaranteed to honor 'num_segments'\nvoid ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, int num_segments)\n{\n    if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2)\n        return;\n\n    // Because we are filling a closed shape we remove 1 from the count of segments/points\n    const float a_max = (IM_PI * 2.0f) * ((float)num_segments - 1.0f) / (float)num_segments;\n    PathArcTo(center, radius, 0.0f, a_max, num_segments - 1);\n    PathFillConvex(col);\n}\n\n// Ellipse\nvoid ImDrawList::AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments, float thickness)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    if (num_segments <= 0)\n        num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.\n\n    // Because we are filling a closed shape we remove 1 from the count of segments/points\n    const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;\n    PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1);\n    PathStroke(col, true, thickness);\n}\n\nvoid ImDrawList::AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    if (num_segments <= 0)\n        num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.\n\n    // Because we are filling a closed shape we remove 1 from the count of segments/points\n    const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;\n    PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1);\n    PathFillConvex(col);\n}\n\n// Cubic Bezier takes 4 controls points\nvoid ImDrawList::AddBezierCubic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, ImU32 col, float thickness, int num_segments)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathBezierCubicCurveTo(p2, p3, p4, num_segments);\n    PathStroke(col, 0, thickness);\n}\n\n// Quadratic Bezier takes 3 controls points\nvoid ImDrawList::AddBezierQuadratic(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, ImU32 col, float thickness, int num_segments)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    PathLineTo(p1);\n    PathBezierQuadraticCurveTo(p2, p3, num_segments);\n    PathStroke(col, 0, thickness);\n}\n\nvoid ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    if (text_end == NULL)\n        text_end = text_begin + strlen(text_begin);\n    if (text_begin == text_end)\n        return;\n\n    // Pull default font/size from the shared ImDrawListSharedData instance\n    if (font == NULL)\n        font = _Data->Font;\n    if (font_size == 0.0f)\n        font_size = _Data->FontSize;\n\n    IM_ASSERT(font->ContainerAtlas->TexID == _CmdHeader.TextureId);  // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font.\n\n    ImVec4 clip_rect = _CmdHeader.ClipRect;\n    if (cpu_fine_clip_rect)\n    {\n        clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x);\n        clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y);\n        clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);\n        clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);\n    }\n    font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL);\n}\n\nvoid ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end)\n{\n    AddText(NULL, 0.0f, pos, col, text_begin, text_end);\n}\n\nvoid ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    const bool push_texture_id = user_texture_id != _CmdHeader.TextureId;\n    if (push_texture_id)\n        PushTextureID(user_texture_id);\n\n    PrimReserve(6, 4);\n    PrimRectUV(p_min, p_max, uv_min, uv_max, col);\n\n    if (push_texture_id)\n        PopTextureID();\n}\n\nvoid ImDrawList::AddImageQuad(ImTextureID user_texture_id, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& uv1, const ImVec2& uv2, const ImVec2& uv3, const ImVec2& uv4, ImU32 col)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    const bool push_texture_id = user_texture_id != _CmdHeader.TextureId;\n    if (push_texture_id)\n        PushTextureID(user_texture_id);\n\n    PrimReserve(6, 4);\n    PrimQuadUV(p1, p2, p3, p4, uv1, uv2, uv3, uv4, col);\n\n    if (push_texture_id)\n        PopTextureID();\n}\n\nvoid ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col, float rounding, ImDrawFlags flags)\n{\n    if ((col & IM_COL32_A_MASK) == 0)\n        return;\n\n    flags = FixRectCornerFlags(flags);\n    if (rounding < 0.5f || (flags & ImDrawFlags_RoundCornersMask_) == ImDrawFlags_RoundCornersNone)\n    {\n        AddImage(user_texture_id, p_min, p_max, uv_min, uv_max, col);\n        return;\n    }\n\n    const bool push_texture_id = user_texture_id != _CmdHeader.TextureId;\n    if (push_texture_id)\n        PushTextureID(user_texture_id);\n\n    int vert_start_idx = VtxBuffer.Size;\n    PathRect(p_min, p_max, rounding, flags);\n    PathFillConvex(col);\n    int vert_end_idx = VtxBuffer.Size;\n    ImGui::ShadeVertsLinearUV(this, vert_start_idx, vert_end_idx, p_min, p_max, uv_min, uv_max, true);\n\n    if (push_texture_id)\n        PopTextureID();\n}\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImDrawListSplitter\n//-----------------------------------------------------------------------------\n// FIXME: This may be a little confusing, trying to be a little too low-level/optimal instead of just doing vector swap..\n//-----------------------------------------------------------------------------\n\nvoid ImDrawListSplitter::ClearFreeMemory()\n{\n    for (int i = 0; i < _Channels.Size; i++)\n    {\n        if (i == _Current)\n            memset(&_Channels[i], 0, sizeof(_Channels[i]));  // Current channel is a copy of CmdBuffer/IdxBuffer, don't destruct again\n        _Channels[i]._CmdBuffer.clear();\n        _Channels[i]._IdxBuffer.clear();\n    }\n    _Current = 0;\n    _Count = 1;\n    _Channels.clear();\n}\n\nvoid ImDrawListSplitter::Split(ImDrawList* draw_list, int channels_count)\n{\n    IM_UNUSED(draw_list);\n    IM_ASSERT(_Current == 0 && _Count <= 1 && \"Nested channel splitting is not supported. Please use separate instances of ImDrawListSplitter.\");\n    int old_channels_count = _Channels.Size;\n    if (old_channels_count < channels_count)\n    {\n        _Channels.reserve(channels_count); // Avoid over reserving since this is likely to stay stable\n        _Channels.resize(channels_count);\n    }\n    _Count = channels_count;\n\n    // Channels[] (24/32 bytes each) hold storage that we'll swap with draw_list->_CmdBuffer/_IdxBuffer\n    // The content of Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to.\n    // When we switch to the next channel, we'll copy draw_list->_CmdBuffer/_IdxBuffer into Channels[0] and then Channels[1] into draw_list->CmdBuffer/_IdxBuffer\n    memset(&_Channels[0], 0, sizeof(ImDrawChannel));\n    for (int i = 1; i < channels_count; i++)\n    {\n        if (i >= old_channels_count)\n        {\n            IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel();\n        }\n        else\n        {\n            _Channels[i]._CmdBuffer.resize(0);\n            _Channels[i]._IdxBuffer.resize(0);\n        }\n    }\n}\n\nvoid ImDrawListSplitter::Merge(ImDrawList* draw_list)\n{\n    // Note that we never use or rely on _Channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use.\n    if (_Count <= 1)\n        return;\n\n    SetCurrentChannel(draw_list, 0);\n    draw_list->_PopUnusedDrawCmd();\n\n    // Calculate our final buffer sizes. Also fix the incorrect IdxOffset values in each command.\n    int new_cmd_buffer_count = 0;\n    int new_idx_buffer_count = 0;\n    ImDrawCmd* last_cmd = (_Count > 0 && draw_list->CmdBuffer.Size > 0) ? &draw_list->CmdBuffer.back() : NULL;\n    int idx_offset = last_cmd ? last_cmd->IdxOffset + last_cmd->ElemCount : 0;\n    for (int i = 1; i < _Count; i++)\n    {\n        ImDrawChannel& ch = _Channels[i];\n        if (ch._CmdBuffer.Size > 0 && ch._CmdBuffer.back().ElemCount == 0 && ch._CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd()\n            ch._CmdBuffer.pop_back();\n\n        if (ch._CmdBuffer.Size > 0 && last_cmd != NULL)\n        {\n            // Do not include ImDrawCmd_AreSequentialIdxOffset() in the compare as we rebuild IdxOffset values ourselves.\n            // Manipulating IdxOffset (e.g. by reordering draw commands like done by RenderDimmedBackgroundBehindWindow()) is not supported within a splitter.\n            ImDrawCmd* next_cmd = &ch._CmdBuffer[0];\n            if (ImDrawCmd_HeaderCompare(last_cmd, next_cmd) == 0 && last_cmd->UserCallback == NULL && next_cmd->UserCallback == NULL)\n            {\n                // Merge previous channel last draw command with current channel first draw command if matching.\n                last_cmd->ElemCount += next_cmd->ElemCount;\n                idx_offset += next_cmd->ElemCount;\n                ch._CmdBuffer.erase(ch._CmdBuffer.Data); // FIXME-OPT: Improve for multiple merges.\n            }\n        }\n        if (ch._CmdBuffer.Size > 0)\n            last_cmd = &ch._CmdBuffer.back();\n        new_cmd_buffer_count += ch._CmdBuffer.Size;\n        new_idx_buffer_count += ch._IdxBuffer.Size;\n        for (int cmd_n = 0; cmd_n < ch._CmdBuffer.Size; cmd_n++)\n        {\n            ch._CmdBuffer.Data[cmd_n].IdxOffset = idx_offset;\n            idx_offset += ch._CmdBuffer.Data[cmd_n].ElemCount;\n        }\n    }\n    draw_list->CmdBuffer.resize(draw_list->CmdBuffer.Size + new_cmd_buffer_count);\n    draw_list->IdxBuffer.resize(draw_list->IdxBuffer.Size + new_idx_buffer_count);\n\n    // Write commands and indices in order (they are fairly small structures, we don't copy vertices only indices)\n    ImDrawCmd* cmd_write = draw_list->CmdBuffer.Data + draw_list->CmdBuffer.Size - new_cmd_buffer_count;\n    ImDrawIdx* idx_write = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size - new_idx_buffer_count;\n    for (int i = 1; i < _Count; i++)\n    {\n        ImDrawChannel& ch = _Channels[i];\n        if (int sz = ch._CmdBuffer.Size) { memcpy(cmd_write, ch._CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; }\n        if (int sz = ch._IdxBuffer.Size) { memcpy(idx_write, ch._IdxBuffer.Data, sz * sizeof(ImDrawIdx)); idx_write += sz; }\n    }\n    draw_list->_IdxWritePtr = idx_write;\n\n    // Ensure there's always a non-callback draw command trailing the command-buffer\n    if (draw_list->CmdBuffer.Size == 0 || draw_list->CmdBuffer.back().UserCallback != NULL)\n        draw_list->AddDrawCmd();\n\n    // If current command is used with different settings we need to add a new command\n    ImDrawCmd* curr_cmd = &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1];\n    if (curr_cmd->ElemCount == 0)\n        ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset\n    else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0)\n        draw_list->AddDrawCmd();\n\n    _Count = 1;\n}\n\nvoid ImDrawListSplitter::SetCurrentChannel(ImDrawList* draw_list, int idx)\n{\n    IM_ASSERT(idx >= 0 && idx < _Count);\n    if (_Current == idx)\n        return;\n\n    // Overwrite ImVector (12/16 bytes), four times. This is merely a silly optimization instead of doing .swap()\n    memcpy(&_Channels.Data[_Current]._CmdBuffer, &draw_list->CmdBuffer, sizeof(draw_list->CmdBuffer));\n    memcpy(&_Channels.Data[_Current]._IdxBuffer, &draw_list->IdxBuffer, sizeof(draw_list->IdxBuffer));\n    _Current = idx;\n    memcpy(&draw_list->CmdBuffer, &_Channels.Data[idx]._CmdBuffer, sizeof(draw_list->CmdBuffer));\n    memcpy(&draw_list->IdxBuffer, &_Channels.Data[idx]._IdxBuffer, sizeof(draw_list->IdxBuffer));\n    draw_list->_IdxWritePtr = draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size;\n\n    // If current command is used with different settings we need to add a new command\n    ImDrawCmd* curr_cmd = (draw_list->CmdBuffer.Size == 0) ? NULL : &draw_list->CmdBuffer.Data[draw_list->CmdBuffer.Size - 1];\n    if (curr_cmd == NULL)\n        draw_list->AddDrawCmd();\n    else if (curr_cmd->ElemCount == 0)\n        ImDrawCmd_HeaderCopy(curr_cmd, &draw_list->_CmdHeader); // Copy ClipRect, TextureId, VtxOffset\n    else if (ImDrawCmd_HeaderCompare(curr_cmd, &draw_list->_CmdHeader) != 0)\n        draw_list->AddDrawCmd();\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImDrawData\n//-----------------------------------------------------------------------------\n\nvoid ImDrawData::Clear()\n{\n    Valid = false;\n    CmdListsCount = TotalIdxCount = TotalVtxCount = 0;\n    CmdLists.resize(0); // The ImDrawList are NOT owned by ImDrawData but e.g. by ImGuiContext, so we don't clear them.\n    DisplayPos = DisplaySize = FramebufferScale = ImVec2(0.0f, 0.0f);\n    OwnerViewport = NULL;\n}\n\n// Important: 'out_list' is generally going to be draw_data->CmdLists, but may be another temporary list\n// as long at it is expected that the result will be later merged into draw_data->CmdLists[].\nvoid ImGui::AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector<ImDrawList*>* out_list, ImDrawList* draw_list)\n{\n    if (draw_list->CmdBuffer.Size == 0)\n        return;\n    if (draw_list->CmdBuffer.Size == 1 && draw_list->CmdBuffer[0].ElemCount == 0 && draw_list->CmdBuffer[0].UserCallback == NULL)\n        return;\n\n    // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc.\n    // May trigger for you if you are using PrimXXX functions incorrectly.\n    IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size);\n    IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size);\n    if (!(draw_list->Flags & ImDrawListFlags_AllowVtxOffset))\n        IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size);\n\n    // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window)\n    // If this assert triggers because you are drawing lots of stuff manually:\n    // - First, make sure you are coarse clipping yourself and not trying to draw many things outside visible bounds.\n    //   Be mindful that the lower-level ImDrawList API doesn't filter vertices. Use the Metrics/Debugger window to inspect draw list contents.\n    // - If you want large meshes with more than 64K vertices, you can either:\n    //   (A) Handle the ImDrawCmd::VtxOffset value in your renderer backend, and set 'io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset'.\n    //       Most example backends already support this from 1.71. Pre-1.71 backends won't.\n    //       Some graphics API such as GL ES 1/2 don't have a way to offset the starting vertex so it is not supported for them.\n    //   (B) Or handle 32-bit indices in your renderer backend, and uncomment '#define ImDrawIdx unsigned int' line in imconfig.h.\n    //       Most example backends already support this. For example, the OpenGL example code detect index size at compile-time:\n    //         glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset);\n    //       Your own engine or render API may use different parameters or function calls to specify index sizes.\n    //       2 and 4 bytes indices are generally supported by most graphics API.\n    // - If for some reason neither of those solutions works for you, a workaround is to call BeginChild()/EndChild() before reaching\n    //   the 64K limit to split your draw commands in multiple draw lists.\n    if (sizeof(ImDrawIdx) == 2)\n        IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && \"Too many vertices in ImDrawList using 16-bit indices. Read comment above\");\n\n    // Add to output list + records state in ImDrawData\n    out_list->push_back(draw_list);\n    draw_data->CmdListsCount++;\n    draw_data->TotalVtxCount += draw_list->VtxBuffer.Size;\n    draw_data->TotalIdxCount += draw_list->IdxBuffer.Size;\n}\n\nvoid ImDrawData::AddDrawList(ImDrawList* draw_list)\n{\n    IM_ASSERT(CmdLists.Size == CmdListsCount);\n    draw_list->_PopUnusedDrawCmd();\n    ImGui::AddDrawListToDrawDataEx(this, &CmdLists, draw_list);\n}\n\n// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!\nvoid ImDrawData::DeIndexAllBuffers()\n{\n    ImVector<ImDrawVert> new_vtx_buffer;\n    TotalVtxCount = TotalIdxCount = 0;\n    for (int i = 0; i < CmdListsCount; i++)\n    {\n        ImDrawList* cmd_list = CmdLists[i];\n        if (cmd_list->IdxBuffer.empty())\n            continue;\n        new_vtx_buffer.resize(cmd_list->IdxBuffer.Size);\n        for (int j = 0; j < cmd_list->IdxBuffer.Size; j++)\n            new_vtx_buffer[j] = cmd_list->VtxBuffer[cmd_list->IdxBuffer[j]];\n        cmd_list->VtxBuffer.swap(new_vtx_buffer);\n        cmd_list->IdxBuffer.resize(0);\n        TotalVtxCount += cmd_list->VtxBuffer.Size;\n    }\n}\n\n// Helper to scale the ClipRect field of each ImDrawCmd.\n// Use if your final output buffer is at a different scale than draw_data->DisplaySize,\n// or if there is a difference between your window resolution and framebuffer resolution.\nvoid ImDrawData::ScaleClipRects(const ImVec2& fb_scale)\n{\n    for (ImDrawList* draw_list : CmdLists)\n        for (ImDrawCmd& cmd : draw_list->CmdBuffer)\n            cmd.ClipRect = ImVec4(cmd.ClipRect.x * fb_scale.x, cmd.ClipRect.y * fb_scale.y, cmd.ClipRect.z * fb_scale.x, cmd.ClipRect.w * fb_scale.y);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Helpers ShadeVertsXXX functions\n//-----------------------------------------------------------------------------\n\n// Generic linear color gradient, write to RGB fields, leave A untouched.\nvoid ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1)\n{\n    ImVec2 gradient_extent = gradient_p1 - gradient_p0;\n    float gradient_inv_length2 = 1.0f / ImLengthSqr(gradient_extent);\n    ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;\n    ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;\n    const int col0_r = (int)(col0 >> IM_COL32_R_SHIFT) & 0xFF;\n    const int col0_g = (int)(col0 >> IM_COL32_G_SHIFT) & 0xFF;\n    const int col0_b = (int)(col0 >> IM_COL32_B_SHIFT) & 0xFF;\n    const int col_delta_r = ((int)(col1 >> IM_COL32_R_SHIFT) & 0xFF) - col0_r;\n    const int col_delta_g = ((int)(col1 >> IM_COL32_G_SHIFT) & 0xFF) - col0_g;\n    const int col_delta_b = ((int)(col1 >> IM_COL32_B_SHIFT) & 0xFF) - col0_b;\n    for (ImDrawVert* vert = vert_start; vert < vert_end; vert++)\n    {\n        float d = ImDot(vert->pos - gradient_p0, gradient_extent);\n        float t = ImClamp(d * gradient_inv_length2, 0.0f, 1.0f);\n        int r = (int)(col0_r + col_delta_r * t);\n        int g = (int)(col0_g + col_delta_g * t);\n        int b = (int)(col0_b + col_delta_b * t);\n        vert->col = (r << IM_COL32_R_SHIFT) | (g << IM_COL32_G_SHIFT) | (b << IM_COL32_B_SHIFT) | (vert->col & IM_COL32_A_MASK);\n    }\n}\n\n// Distribute UV over (a, b) rectangle\nvoid ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp)\n{\n    const ImVec2 size = b - a;\n    const ImVec2 uv_size = uv_b - uv_a;\n    const ImVec2 scale = ImVec2(\n        size.x != 0.0f ? (uv_size.x / size.x) : 0.0f,\n        size.y != 0.0f ? (uv_size.y / size.y) : 0.0f);\n\n    ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;\n    ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;\n    if (clamp)\n    {\n        const ImVec2 min = ImMin(uv_a, uv_b);\n        const ImVec2 max = ImMax(uv_a, uv_b);\n        for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)\n            vertex->uv = ImClamp(uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale), min, max);\n    }\n    else\n    {\n        for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)\n            vertex->uv = uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale);\n    }\n}\n\nvoid ImGui::ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out)\n{\n    ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;\n    ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;\n    for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)\n        vertex->pos = ImRotate(vertex->pos- pivot_in, cos_a, sin_a) + pivot_out;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFontConfig\n//-----------------------------------------------------------------------------\n\nImFontConfig::ImFontConfig()\n{\n    memset(this, 0, sizeof(*this));\n    FontDataOwnedByAtlas = true;\n    OversampleH = 2;\n    OversampleV = 1;\n    GlyphMaxAdvanceX = FLT_MAX;\n    RasterizerMultiply = 1.0f;\n    RasterizerDensity = 1.0f;\n    EllipsisChar = (ImWchar)-1;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFontAtlas\n//-----------------------------------------------------------------------------\n\n// A work of art lies ahead! (. = white layer, X = black layer, others are blank)\n// The 2x2 white texels on the top left are the ones we'll use everywhere in Dear ImGui to render filled shapes.\n// (This is used when io.MouseDrawCursor = true)\nconst int FONT_ATLAS_DEFAULT_TEX_DATA_W = 122; // Actual texture will be 2 times that + 1 spacing.\nconst int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27;\nstatic const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] =\n{\n    \"..-         -XXXXXXX-    X    -           X           -XXXXXXX          -          XXXXXXX-     XX          - XX       XX \"\n    \"..-         -X.....X-   X.X   -          X.X          -X.....X          -          X.....X-    X..X         -X..X     X..X\"\n    \"---         -XXX.XXX-  X...X  -         X...X         -X....X           -           X....X-    X..X         -X...X   X...X\"\n    \"X           -  X.X  - X.....X -        X.....X        -X...X            -            X...X-    X..X         - X...X X...X \"\n    \"XX          -  X.X  -X.......X-       X.......X       -X..X.X           -           X.X..X-    X..X         -  X...X...X  \"\n    \"X.X         -  X.X  -XXXX.XXXX-       XXXX.XXXX       -X.X X.X          -          X.X X.X-    X..XXX       -   X.....X   \"\n    \"X..X        -  X.X  -   X.X   -          X.X          -XX   X.X         -         X.X   XX-    X..X..XXX    -    X...X    \"\n    \"X...X       -  X.X  -   X.X   -    XX    X.X    XX    -      X.X        -        X.X      -    X..X..X..XX  -     X.X     \"\n    \"X....X      -  X.X  -   X.X   -   X.X    X.X    X.X   -       X.X       -       X.X       -    X..X..X..X.X -    X...X    \"\n    \"X.....X     -  X.X  -   X.X   -  X..X    X.X    X..X  -        X.X      -      X.X        -XXX X..X..X..X..X-   X.....X   \"\n    \"X......X    -  X.X  -   X.X   - X...XXXXXX.XXXXXX...X -         X.X   XX-XX   X.X         -X..XX........X..X-  X...X...X  \"\n    \"X.......X   -  X.X  -   X.X   -X.....................X-          X.X X.X-X.X X.X          -X...X...........X- X...X X...X \"\n    \"X........X  -  X.X  -   X.X   - X...XXXXXX.XXXXXX...X -           X.X..X-X..X.X           - X..............X-X...X   X...X\"\n    \"X.........X -XXX.XXX-   X.X   -  X..X    X.X    X..X  -            X...X-X...X            -  X.............X-X..X     X..X\"\n    \"X..........X-X.....X-   X.X   -   X.X    X.X    X.X   -           X....X-X....X           -  X.............X- XX       XX \"\n    \"X......XXXXX-XXXXXXX-   X.X   -    XX    X.X    XX    -          X.....X-X.....X          -   X............X--------------\"\n    \"X...X..X    ---------   X.X   -          X.X          -          XXXXXXX-XXXXXXX          -   X...........X -             \"\n    \"X..X X..X   -       -XXXX.XXXX-       XXXX.XXXX       -------------------------------------    X..........X -             \"\n    \"X.X  X..X   -       -X.......X-       X.......X       -    XX           XX    -           -    X..........X -             \"\n    \"XX    X..X  -       - X.....X -        X.....X        -   X.X           X.X   -           -     X........X  -             \"\n    \"      X..X  -       -  X...X  -         X...X         -  X..X           X..X  -           -     X........X  -             \"\n    \"       XX   -       -   X.X   -          X.X          - X...XXXXXXXXXXXXX...X -           -     XXXXXXXXXX  -             \"\n    \"-------------       -    X    -           X           -X.....................X-           -------------------             \"\n    \"                    ----------------------------------- X...XXXXXXXXXXXXX...X -                                           \"\n    \"                                                      -  X..X           X..X  -                                           \"\n    \"                                                      -   X.X           X.X   -                                           \"\n    \"                                                      -    XX           XX    -                                           \"\n};\n\nstatic const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] =\n{\n    // Pos ........ Size ......... Offset ......\n    { ImVec2( 0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow\n    { ImVec2(13,0), ImVec2( 7,16), ImVec2( 1, 8) }, // ImGuiMouseCursor_TextInput\n    { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_ResizeAll\n    { ImVec2(21,0), ImVec2( 9,23), ImVec2( 4,11) }, // ImGuiMouseCursor_ResizeNS\n    { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 4) }, // ImGuiMouseCursor_ResizeEW\n    { ImVec2(73,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNESW\n    { ImVec2(55,0), ImVec2(17,17), ImVec2( 8, 8) }, // ImGuiMouseCursor_ResizeNWSE\n    { ImVec2(91,0), ImVec2(17,22), ImVec2( 5, 0) }, // ImGuiMouseCursor_Hand\n    { ImVec2(109,0),ImVec2(13,15), ImVec2( 6, 7) }, // ImGuiMouseCursor_NotAllowed\n};\n\nImFontAtlas::ImFontAtlas()\n{\n    memset(this, 0, sizeof(*this));\n    TexGlyphPadding = 1;\n    PackIdMouseCursors = PackIdLines = -1;\n}\n\nImFontAtlas::~ImFontAtlas()\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!\");\n    Clear();\n}\n\nvoid    ImFontAtlas::ClearInputData()\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!\");\n    for (ImFontConfig& font_cfg : ConfigData)\n        if (font_cfg.FontData && font_cfg.FontDataOwnedByAtlas)\n        {\n            IM_FREE(font_cfg.FontData);\n            font_cfg.FontData = NULL;\n        }\n\n    // When clearing this we lose access to the font name and other information used to build the font.\n    for (ImFont* font : Fonts)\n        if (font->ConfigData >= ConfigData.Data && font->ConfigData < ConfigData.Data + ConfigData.Size)\n        {\n            font->ConfigData = NULL;\n            font->ConfigDataCount = 0;\n        }\n    ConfigData.clear();\n    CustomRects.clear();\n    PackIdMouseCursors = PackIdLines = -1;\n    // Important: we leave TexReady untouched\n}\n\nvoid    ImFontAtlas::ClearTexData()\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!\");\n    if (TexPixelsAlpha8)\n        IM_FREE(TexPixelsAlpha8);\n    if (TexPixelsRGBA32)\n        IM_FREE(TexPixelsRGBA32);\n    TexPixelsAlpha8 = NULL;\n    TexPixelsRGBA32 = NULL;\n    TexPixelsUseColors = false;\n    // Important: we leave TexReady untouched\n}\n\nvoid    ImFontAtlas::ClearFonts()\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!\");\n    Fonts.clear_delete();\n    TexReady = false;\n}\n\nvoid    ImFontAtlas::Clear()\n{\n    ClearInputData();\n    ClearTexData();\n    ClearFonts();\n}\n\nvoid    ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)\n{\n    // Build atlas on demand\n    if (TexPixelsAlpha8 == NULL)\n        Build();\n\n    *out_pixels = TexPixelsAlpha8;\n    if (out_width) *out_width = TexWidth;\n    if (out_height) *out_height = TexHeight;\n    if (out_bytes_per_pixel) *out_bytes_per_pixel = 1;\n}\n\nvoid    ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)\n{\n    // Convert to RGBA32 format on demand\n    // Although it is likely to be the most commonly used format, our font rendering is 1 channel / 8 bpp\n    if (!TexPixelsRGBA32)\n    {\n        unsigned char* pixels = NULL;\n        GetTexDataAsAlpha8(&pixels, NULL, NULL);\n        if (pixels)\n        {\n            TexPixelsRGBA32 = (unsigned int*)IM_ALLOC((size_t)TexWidth * (size_t)TexHeight * 4);\n            const unsigned char* src = pixels;\n            unsigned int* dst = TexPixelsRGBA32;\n            for (int n = TexWidth * TexHeight; n > 0; n--)\n                *dst++ = IM_COL32(255, 255, 255, (unsigned int)(*src++));\n        }\n    }\n\n    *out_pixels = (unsigned char*)TexPixelsRGBA32;\n    if (out_width) *out_width = TexWidth;\n    if (out_height) *out_height = TexHeight;\n    if (out_bytes_per_pixel) *out_bytes_per_pixel = 4;\n}\n\nImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!\");\n    IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0);\n    IM_ASSERT(font_cfg->SizePixels > 0.0f);\n\n    // Create new font\n    if (!font_cfg->MergeMode)\n        Fonts.push_back(IM_NEW(ImFont));\n    else\n        IM_ASSERT(!Fonts.empty() && \"Cannot use MergeMode for the first font\"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.\n\n    ConfigData.push_back(*font_cfg);\n    ImFontConfig& new_font_cfg = ConfigData.back();\n    if (new_font_cfg.DstFont == NULL)\n        new_font_cfg.DstFont = Fonts.back();\n    if (!new_font_cfg.FontDataOwnedByAtlas)\n    {\n        new_font_cfg.FontData = IM_ALLOC(new_font_cfg.FontDataSize);\n        new_font_cfg.FontDataOwnedByAtlas = true;\n        memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize);\n    }\n\n    if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)\n        new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;\n\n    ImFontAtlasUpdateConfigDataPointers(this);\n\n    // Invalidate texture\n    TexReady = false;\n    ClearTexData();\n    return new_font_cfg.DstFont;\n}\n\n// Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder)\nstatic unsigned int stb_decompress_length(const unsigned char* input);\nstatic unsigned int stb_decompress(unsigned char* output, const unsigned char* input, unsigned int length);\nstatic const char*  GetDefaultCompressedFontDataTTFBase85();\nstatic unsigned int Decode85Byte(char c)                                    { return c >= '\\\\' ? c-36 : c-35; }\nstatic void         Decode85(const unsigned char* src, unsigned char* dst)\n{\n    while (*src)\n    {\n        unsigned int tmp = Decode85Byte(src[0]) + 85 * (Decode85Byte(src[1]) + 85 * (Decode85Byte(src[2]) + 85 * (Decode85Byte(src[3]) + 85 * Decode85Byte(src[4]))));\n        dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF);   // We can't assume little-endianness.\n        src += 5;\n        dst += 4;\n    }\n}\n\n// Load embedded ProggyClean.ttf at size 13, disable oversampling\nImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)\n{\n    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();\n    if (!font_cfg_template)\n    {\n        font_cfg.OversampleH = font_cfg.OversampleV = 1;\n        font_cfg.PixelSnapH = true;\n    }\n    if (font_cfg.SizePixels <= 0.0f)\n        font_cfg.SizePixels = 13.0f * 1.0f;\n    if (font_cfg.Name[0] == '\\0')\n        ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), \"ProggyClean.ttf, %dpx\", (int)font_cfg.SizePixels);\n    font_cfg.EllipsisChar = (ImWchar)0x0085;\n    font_cfg.GlyphOffset.y = 1.0f * IM_TRUNC(font_cfg.SizePixels / 13.0f);  // Add +1 offset per 13 units\n\n    const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();\n    const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();\n    ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, glyph_ranges);\n    return font;\n}\n\nImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!\");\n    size_t data_size = 0;\n    void* data = ImFileLoadToMemory(filename, \"rb\", &data_size, 0);\n    if (!data)\n    {\n        IM_ASSERT_USER_ERROR(0, \"Could not load font file!\");\n        return NULL;\n    }\n    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();\n    if (font_cfg.Name[0] == '\\0')\n    {\n        // Store a short copy of filename into into the font name for convenience\n        const char* p;\n        for (p = filename + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\\\'; p--) {}\n        ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), \"%s, %.0fpx\", p, size_pixels);\n    }\n    return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges);\n}\n\n// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build().\nImFont* ImFontAtlas::AddFontFromMemoryTTF(void* font_data, int font_data_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!\");\n    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();\n    IM_ASSERT(font_cfg.FontData == NULL);\n    IM_ASSERT(font_data_size > 100 && \"Incorrect value for font_data_size!\"); // Heuristic to prevent accidentally passing a wrong value to font_data_size.\n    font_cfg.FontData = font_data;\n    font_cfg.FontDataSize = font_data_size;\n    font_cfg.SizePixels = size_pixels > 0.0f ? size_pixels : font_cfg.SizePixels;\n    if (glyph_ranges)\n        font_cfg.GlyphRanges = glyph_ranges;\n    return AddFont(&font_cfg);\n}\n\nImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)\n{\n    const unsigned int buf_decompressed_size = stb_decompress_length((const unsigned char*)compressed_ttf_data);\n    unsigned char* buf_decompressed_data = (unsigned char*)IM_ALLOC(buf_decompressed_size);\n    stb_decompress(buf_decompressed_data, (const unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size);\n\n    ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();\n    IM_ASSERT(font_cfg.FontData == NULL);\n    font_cfg.FontDataOwnedByAtlas = true;\n    return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, &font_cfg, glyph_ranges);\n}\n\nImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges)\n{\n    int compressed_ttf_size = (((int)strlen(compressed_ttf_data_base85) + 4) / 5) * 4;\n    void* compressed_ttf = IM_ALLOC((size_t)compressed_ttf_size);\n    Decode85((const unsigned char*)compressed_ttf_data_base85, (unsigned char*)compressed_ttf);\n    ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges);\n    IM_FREE(compressed_ttf);\n    return font;\n}\n\nint ImFontAtlas::AddCustomRectRegular(int width, int height)\n{\n    IM_ASSERT(width > 0 && width <= 0xFFFF);\n    IM_ASSERT(height > 0 && height <= 0xFFFF);\n    ImFontAtlasCustomRect r;\n    r.Width = (unsigned short)width;\n    r.Height = (unsigned short)height;\n    CustomRects.push_back(r);\n    return CustomRects.Size - 1; // Return index\n}\n\nint ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset)\n{\n#ifdef IMGUI_USE_WCHAR32\n    IM_ASSERT(id <= IM_UNICODE_CODEPOINT_MAX);\n#endif\n    IM_ASSERT(font != NULL);\n    IM_ASSERT(width > 0 && width <= 0xFFFF);\n    IM_ASSERT(height > 0 && height <= 0xFFFF);\n    ImFontAtlasCustomRect r;\n    r.Width = (unsigned short)width;\n    r.Height = (unsigned short)height;\n    r.GlyphID = id;\n    r.GlyphAdvanceX = advance_x;\n    r.GlyphOffset = offset;\n    r.Font = font;\n    CustomRects.push_back(r);\n    return CustomRects.Size - 1; // Return index\n}\n\nvoid ImFontAtlas::CalcCustomRectUV(const ImFontAtlasCustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) const\n{\n    IM_ASSERT(TexWidth > 0 && TexHeight > 0);   // Font atlas needs to be built before we can calculate UV coordinates\n    IM_ASSERT(rect->IsPacked());                // Make sure the rectangle has been packed\n    *out_uv_min = ImVec2((float)rect->X * TexUvScale.x, (float)rect->Y * TexUvScale.y);\n    *out_uv_max = ImVec2((float)(rect->X + rect->Width) * TexUvScale.x, (float)(rect->Y + rect->Height) * TexUvScale.y);\n}\n\nbool ImFontAtlas::GetMouseCursorTexData(ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2])\n{\n    if (cursor_type <= ImGuiMouseCursor_None || cursor_type >= ImGuiMouseCursor_COUNT)\n        return false;\n    if (Flags & ImFontAtlasFlags_NoMouseCursors)\n        return false;\n\n    IM_ASSERT(PackIdMouseCursors != -1);\n    ImFontAtlasCustomRect* r = GetCustomRectByIndex(PackIdMouseCursors);\n    ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r->X, (float)r->Y);\n    ImVec2 size = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][1];\n    *out_size = size;\n    *out_offset = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][2];\n    out_uv_border[0] = (pos) * TexUvScale;\n    out_uv_border[1] = (pos + size) * TexUvScale;\n    pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W + 1;\n    out_uv_fill[0] = (pos) * TexUvScale;\n    out_uv_fill[1] = (pos + size) * TexUvScale;\n    return true;\n}\n\nbool    ImFontAtlas::Build()\n{\n    IM_ASSERT(!Locked && \"Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!\");\n\n    // Default font is none are specified\n    if (ConfigData.Size == 0)\n        AddFontDefault();\n\n    // Select builder\n    // - Note that we do not reassign to atlas->FontBuilderIO, since it is likely to point to static data which\n    //   may mess with some hot-reloading schemes. If you need to assign to this (for dynamic selection) AND are\n    //   using a hot-reloading scheme that messes up static data, store your own instance of ImFontBuilderIO somewhere\n    //   and point to it instead of pointing directly to return value of the GetBuilderXXX functions.\n    const ImFontBuilderIO* builder_io = FontBuilderIO;\n    if (builder_io == NULL)\n    {\n#ifdef IMGUI_ENABLE_FREETYPE\n        builder_io = ImGuiFreeType::GetBuilderForFreeType();\n#elif defined(IMGUI_ENABLE_STB_TRUETYPE)\n        builder_io = ImFontAtlasGetBuilderForStbTruetype();\n#else\n        IM_ASSERT(0); // Invalid Build function\n#endif\n    }\n\n    // Build\n    return builder_io->FontBuilder_Build(this);\n}\n\nvoid    ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_brighten_factor)\n{\n    for (unsigned int i = 0; i < 256; i++)\n    {\n        unsigned int value = (unsigned int)(i * in_brighten_factor);\n        out_table[i] = value > 255 ? 255 : (value & 0xFF);\n    }\n}\n\nvoid    ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride)\n{\n    IM_ASSERT_PARANOID(w <= stride);\n    unsigned char* data = pixels + x + y * stride;\n    for (int j = h; j > 0; j--, data += stride - w)\n        for (int i = w; i > 0; i--, data++)\n            *data = table[*data];\n}\n\n#ifdef IMGUI_ENABLE_STB_TRUETYPE\n// Temporary data for one source font (multiple source fonts can be merged into one destination ImFont)\n// (C++03 doesn't allow instancing ImVector<> with function-local types so we declare the type here.)\nstruct ImFontBuildSrcData\n{\n    stbtt_fontinfo      FontInfo;\n    stbtt_pack_range    PackRange;          // Hold the list of codepoints to pack (essentially points to Codepoints.Data)\n    stbrp_rect*         Rects;              // Rectangle to pack. We first fill in their size and the packer will give us their position.\n    stbtt_packedchar*   PackedChars;        // Output glyphs\n    const ImWchar*      SrcRanges;          // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)\n    int                 DstIndex;           // Index into atlas->Fonts[] and dst_tmp_array[]\n    int                 GlyphsHighest;      // Highest requested codepoint\n    int                 GlyphsCount;        // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)\n    ImBitVector         GlyphsSet;          // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)\n    ImVector<int>       GlyphsList;         // Glyph codepoints list (flattened version of GlyphsSet)\n};\n\n// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)\nstruct ImFontBuildDstData\n{\n    int                 SrcCount;           // Number of source fonts targeting this destination font.\n    int                 GlyphsHighest;\n    int                 GlyphsCount;\n    ImBitVector         GlyphsSet;          // This is used to resolve collision when multiple sources are merged into a same destination font.\n};\n\nstatic void UnpackBitVectorToFlatIndexList(const ImBitVector* in, ImVector<int>* out)\n{\n    IM_ASSERT(sizeof(in->Storage.Data[0]) == sizeof(int));\n    const ImU32* it_begin = in->Storage.begin();\n    const ImU32* it_end = in->Storage.end();\n    for (const ImU32* it = it_begin; it < it_end; it++)\n        if (ImU32 entries_32 = *it)\n            for (ImU32 bit_n = 0; bit_n < 32; bit_n++)\n                if (entries_32 & ((ImU32)1 << bit_n))\n                    out->push_back((int)(((it - it_begin) << 5) + bit_n));\n}\n\nstatic bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)\n{\n    IM_ASSERT(atlas->ConfigData.Size > 0);\n\n    ImFontAtlasBuildInit(atlas);\n\n    // Clear atlas\n    atlas->TexID = (ImTextureID)NULL;\n    atlas->TexWidth = atlas->TexHeight = 0;\n    atlas->TexUvScale = ImVec2(0.0f, 0.0f);\n    atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);\n    atlas->ClearTexData();\n\n    // Temporary storage for building\n    ImVector<ImFontBuildSrcData> src_tmp_array;\n    ImVector<ImFontBuildDstData> dst_tmp_array;\n    src_tmp_array.resize(atlas->ConfigData.Size);\n    dst_tmp_array.resize(atlas->Fonts.Size);\n    memset(src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());\n    memset(dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());\n\n    // 1. Initialize font loading structure, check font data validity\n    for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)\n    {\n        ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];\n        ImFontConfig& cfg = atlas->ConfigData[src_i];\n        IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));\n\n        // Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)\n        src_tmp.DstIndex = -1;\n        for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++)\n            if (cfg.DstFont == atlas->Fonts[output_i])\n                src_tmp.DstIndex = output_i;\n        if (src_tmp.DstIndex == -1)\n        {\n            IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?\n            return false;\n        }\n        // Initialize helper structure for font loading and verify that the TTF/OTF data is correct\n        const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);\n        IM_ASSERT(font_offset >= 0 && \"FontData is incorrect, or FontNo cannot be found.\");\n        if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))\n        {\n            IM_ASSERT(0 && \"stbtt_InitFont(): failed to parse FontData. It is correct and complete? Check FontDataSize.\");\n            return false;\n        }\n\n        // Measure highest codepoints\n        ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];\n        src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();\n        for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)\n        {\n            // Check for valid range. This may also help detect *some* dangling pointers, because a common\n            // user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent.\n            IM_ASSERT(src_range[0] <= src_range[1]);\n            src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);\n        }\n        dst_tmp.SrcCount++;\n        dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);\n    }\n\n    // 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs.\n    int total_glyphs_count = 0;\n    for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)\n    {\n        ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];\n        ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];\n        src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1);\n        if (dst_tmp.GlyphsSet.Storage.empty())\n            dst_tmp.GlyphsSet.Create(dst_tmp.GlyphsHighest + 1);\n\n        for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)\n            for (unsigned int codepoint = src_range[0]; codepoint <= src_range[1]; codepoint++)\n            {\n                if (dst_tmp.GlyphsSet.TestBit(codepoint))    // Don't overwrite existing glyphs. We could make this an option for MergeMode (e.g. MergeOverwrite==true)\n                    continue;\n                if (!stbtt_FindGlyphIndex(&src_tmp.FontInfo, codepoint))    // It is actually in the font?\n                    continue;\n\n                // Add to avail set/counters\n                src_tmp.GlyphsCount++;\n                dst_tmp.GlyphsCount++;\n                src_tmp.GlyphsSet.SetBit(codepoint);\n                dst_tmp.GlyphsSet.SetBit(codepoint);\n                total_glyphs_count++;\n            }\n    }\n\n    // 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)\n    for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)\n    {\n        ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];\n        src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);\n        UnpackBitVectorToFlatIndexList(&src_tmp.GlyphsSet, &src_tmp.GlyphsList);\n        src_tmp.GlyphsSet.Clear();\n        IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount);\n    }\n    for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++)\n        dst_tmp_array[dst_i].GlyphsSet.Clear();\n    dst_tmp_array.clear();\n\n    // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)\n    // (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)\n    ImVector<stbrp_rect> buf_rects;\n    ImVector<stbtt_packedchar> buf_packedchars;\n    buf_rects.resize(total_glyphs_count);\n    buf_packedchars.resize(total_glyphs_count);\n    memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes());\n    memset(buf_packedchars.Data, 0, (size_t)buf_packedchars.size_in_bytes());\n\n    // 4. Gather glyphs sizes so we can pack them in our virtual canvas.\n    int total_surface = 0;\n    int buf_rects_out_n = 0;\n    int buf_packedchars_out_n = 0;\n    for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)\n    {\n        ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];\n        if (src_tmp.GlyphsCount == 0)\n            continue;\n\n        src_tmp.Rects = &buf_rects[buf_rects_out_n];\n        src_tmp.PackedChars = &buf_packedchars[buf_packedchars_out_n];\n        buf_rects_out_n += src_tmp.GlyphsCount;\n        buf_packedchars_out_n += src_tmp.GlyphsCount;\n\n        // Convert our ranges in the format stb_truetype wants\n        ImFontConfig& cfg = atlas->ConfigData[src_i];\n        src_tmp.PackRange.font_size = cfg.SizePixels * cfg.RasterizerDensity;\n        src_tmp.PackRange.first_unicode_codepoint_in_range = 0;\n        src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data;\n        src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size;\n        src_tmp.PackRange.chardata_for_range = src_tmp.PackedChars;\n        src_tmp.PackRange.h_oversample = (unsigned char)cfg.OversampleH;\n        src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV;\n\n        // Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects)\n        const float scale = (cfg.SizePixels > 0.0f) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels * cfg.RasterizerDensity) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels * cfg.RasterizerDensity);\n        const int padding = atlas->TexGlyphPadding;\n        for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)\n        {\n            int x0, y0, x1, y1;\n            const int glyph_index_in_font = stbtt_FindGlyphIndex(&src_tmp.FontInfo, src_tmp.GlyphsList[glyph_i]);\n            IM_ASSERT(glyph_index_in_font != 0);\n            stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font, scale * cfg.OversampleH, scale * cfg.OversampleV, 0, 0, &x0, &y0, &x1, &y1);\n            src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + padding + cfg.OversampleH - 1);\n            src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + padding + cfg.OversampleV - 1);\n            total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;\n        }\n    }\n\n    // We need a width for the skyline algorithm, any width!\n    // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.\n    // User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.\n    const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1;\n    atlas->TexHeight = 0;\n    if (atlas->TexDesiredWidth > 0)\n        atlas->TexWidth = atlas->TexDesiredWidth;\n    else\n        atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512;\n\n    // 5. Start packing\n    // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).\n    const int TEX_HEIGHT_MAX = 1024 * 32;\n    stbtt_pack_context spc = {};\n    stbtt_PackBegin(&spc, NULL, atlas->TexWidth, TEX_HEIGHT_MAX, 0, atlas->TexGlyphPadding, NULL);\n    ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info);\n\n    // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.\n    for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)\n    {\n        ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];\n        if (src_tmp.GlyphsCount == 0)\n            continue;\n\n        stbrp_pack_rects((stbrp_context*)spc.pack_info, src_tmp.Rects, src_tmp.GlyphsCount);\n\n        // Extend texture height and mark missing glyphs as non-packed so we won't render them.\n        // FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?)\n        for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)\n            if (src_tmp.Rects[glyph_i].was_packed)\n                atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h);\n    }\n\n    // 7. Allocate texture\n    atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);\n    atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);\n    atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(atlas->TexWidth * atlas->TexHeight);\n    memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);\n    spc.pixels = atlas->TexPixelsAlpha8;\n    spc.height = atlas->TexHeight;\n\n    // 8. Render/rasterize font characters into the texture\n    for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)\n    {\n        ImFontConfig& cfg = atlas->ConfigData[src_i];\n        ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];\n        if (src_tmp.GlyphsCount == 0)\n            continue;\n\n        stbtt_PackFontRangesRenderIntoRects(&spc, &src_tmp.FontInfo, &src_tmp.PackRange, 1, src_tmp.Rects);\n\n        // Apply multiply operator\n        if (cfg.RasterizerMultiply != 1.0f)\n        {\n            unsigned char multiply_table[256];\n            ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);\n            stbrp_rect* r = &src_tmp.Rects[0];\n            for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++, r++)\n                if (r->was_packed)\n                    ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, atlas->TexPixelsAlpha8, r->x, r->y, r->w, r->h, atlas->TexWidth * 1);\n        }\n        src_tmp.Rects = NULL;\n    }\n\n    // End packing\n    stbtt_PackEnd(&spc);\n    buf_rects.clear();\n\n    // 9. Setup ImFont and glyphs for runtime\n    for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)\n    {\n        // When merging fonts with MergeMode=true:\n        // - We can have multiple input fonts writing into a same destination font.\n        // - dst_font->ConfigData is != from cfg which is our source configuration.\n        ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];\n        ImFontConfig& cfg = atlas->ConfigData[src_i];\n        ImFont* dst_font = cfg.DstFont;\n\n        const float font_scale = stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels);\n        int unscaled_ascent, unscaled_descent, unscaled_line_gap;\n        stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);\n\n        const float ascent = ImTrunc(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1));\n        const float descent = ImTrunc(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));\n        ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);\n        const float font_off_x = cfg.GlyphOffset.x;\n        const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);\n\n        const float inv_rasterization_scale = 1.0f / cfg.RasterizerDensity;\n\n        for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)\n        {\n            // Register glyph\n            const int codepoint = src_tmp.GlyphsList[glyph_i];\n            const stbtt_packedchar& pc = src_tmp.PackedChars[glyph_i];\n            stbtt_aligned_quad q;\n            float unused_x = 0.0f, unused_y = 0.0f;\n            stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &unused_x, &unused_y, &q, 0);\n            float x0 = q.x0 * inv_rasterization_scale + font_off_x;\n            float y0 = q.y0 * inv_rasterization_scale + font_off_y;\n            float x1 = q.x1 * inv_rasterization_scale + font_off_x;\n            float y1 = q.y1 * inv_rasterization_scale + font_off_y;\n            dst_font->AddGlyph(&cfg, (ImWchar)codepoint, x0, y0, x1, y1, q.s0, q.t0, q.s1, q.t1, pc.xadvance * inv_rasterization_scale);\n        }\n    }\n\n    // Cleanup\n    src_tmp_array.clear_destruct();\n\n    ImFontAtlasBuildFinish(atlas);\n    return true;\n}\n\nconst ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype()\n{\n    static ImFontBuilderIO io;\n    io.FontBuilder_Build = ImFontAtlasBuildWithStbTruetype;\n    return &io;\n}\n\n#endif // IMGUI_ENABLE_STB_TRUETYPE\n\nvoid ImFontAtlasUpdateConfigDataPointers(ImFontAtlas* atlas)\n{\n    for (ImFontConfig& font_cfg : atlas->ConfigData)\n    {\n        ImFont* font = font_cfg.DstFont;\n        if (!font_cfg.MergeMode)\n        {\n            font->ConfigData = &font_cfg;\n            font->ConfigDataCount = 0;\n        }\n        font->ConfigDataCount++;\n    }\n}\n\nvoid ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent)\n{\n    if (!font_config->MergeMode)\n    {\n        font->ClearOutputData();\n        font->FontSize = font_config->SizePixels;\n        IM_ASSERT(font->ConfigData == font_config);\n        font->ContainerAtlas = atlas;\n        font->Ascent = ascent;\n        font->Descent = descent;\n    }\n}\n\nvoid ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque)\n{\n    stbrp_context* pack_context = (stbrp_context*)stbrp_context_opaque;\n    IM_ASSERT(pack_context != NULL);\n\n    ImVector<ImFontAtlasCustomRect>& user_rects = atlas->CustomRects;\n    IM_ASSERT(user_rects.Size >= 1); // We expect at least the default custom rects to be registered, else something went wrong.\n#ifdef __GNUC__\n    if (user_rects.Size < 1) { __builtin_unreachable(); } // Workaround for GCC bug if IM_ASSERT() is defined to conditionally throw (see #5343)\n#endif\n\n    ImVector<stbrp_rect> pack_rects;\n    pack_rects.resize(user_rects.Size);\n    memset(pack_rects.Data, 0, (size_t)pack_rects.size_in_bytes());\n    for (int i = 0; i < user_rects.Size; i++)\n    {\n        pack_rects[i].w = user_rects[i].Width;\n        pack_rects[i].h = user_rects[i].Height;\n    }\n    stbrp_pack_rects(pack_context, &pack_rects[0], pack_rects.Size);\n    for (int i = 0; i < pack_rects.Size; i++)\n        if (pack_rects[i].was_packed)\n        {\n            user_rects[i].X = (unsigned short)pack_rects[i].x;\n            user_rects[i].Y = (unsigned short)pack_rects[i].y;\n            IM_ASSERT(pack_rects[i].w == user_rects[i].Width && pack_rects[i].h == user_rects[i].Height);\n            atlas->TexHeight = ImMax(atlas->TexHeight, pack_rects[i].y + pack_rects[i].h);\n        }\n}\n\nvoid ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value)\n{\n    IM_ASSERT(x >= 0 && x + w <= atlas->TexWidth);\n    IM_ASSERT(y >= 0 && y + h <= atlas->TexHeight);\n    unsigned char* out_pixel = atlas->TexPixelsAlpha8 + x + (y * atlas->TexWidth);\n    for (int off_y = 0; off_y < h; off_y++, out_pixel += atlas->TexWidth, in_str += w)\n        for (int off_x = 0; off_x < w; off_x++)\n            out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : 0x00;\n}\n\nvoid ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value)\n{\n    IM_ASSERT(x >= 0 && x + w <= atlas->TexWidth);\n    IM_ASSERT(y >= 0 && y + h <= atlas->TexHeight);\n    unsigned int* out_pixel = atlas->TexPixelsRGBA32 + x + (y * atlas->TexWidth);\n    for (int off_y = 0; off_y < h; off_y++, out_pixel += atlas->TexWidth, in_str += w)\n        for (int off_x = 0; off_x < w; off_x++)\n            out_pixel[off_x] = (in_str[off_x] == in_marker_char) ? in_marker_pixel_value : IM_COL32_BLACK_TRANS;\n}\n\nstatic void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas)\n{\n    ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdMouseCursors);\n    IM_ASSERT(r->IsPacked());\n\n    const int w = atlas->TexWidth;\n    if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors))\n    {\n        // Render/copy pixels\n        IM_ASSERT(r->Width == FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1 && r->Height == FONT_ATLAS_DEFAULT_TEX_DATA_H);\n        const int x_for_white = r->X;\n        const int x_for_black = r->X + FONT_ATLAS_DEFAULT_TEX_DATA_W + 1;\n        if (atlas->TexPixelsAlpha8 != NULL)\n        {\n            ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', 0xFF);\n            ImFontAtlasBuildRender8bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', 0xFF);\n        }\n        else\n        {\n            ImFontAtlasBuildRender32bppRectFromString(atlas, x_for_white, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, '.', IM_COL32_WHITE);\n            ImFontAtlasBuildRender32bppRectFromString(atlas, x_for_black, r->Y, FONT_ATLAS_DEFAULT_TEX_DATA_W, FONT_ATLAS_DEFAULT_TEX_DATA_H, FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS, 'X', IM_COL32_WHITE);\n        }\n    }\n    else\n    {\n        // Render 4 white pixels\n        IM_ASSERT(r->Width == 2 && r->Height == 2);\n        const int offset = (int)r->X + (int)r->Y * w;\n        if (atlas->TexPixelsAlpha8 != NULL)\n        {\n            atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF;\n        }\n        else\n        {\n            atlas->TexPixelsRGBA32[offset] = atlas->TexPixelsRGBA32[offset + 1] = atlas->TexPixelsRGBA32[offset + w] = atlas->TexPixelsRGBA32[offset + w + 1] = IM_COL32_WHITE;\n        }\n    }\n    atlas->TexUvWhitePixel = ImVec2((r->X + 0.5f) * atlas->TexUvScale.x, (r->Y + 0.5f) * atlas->TexUvScale.y);\n}\n\nstatic void ImFontAtlasBuildRenderLinesTexData(ImFontAtlas* atlas)\n{\n    if (atlas->Flags & ImFontAtlasFlags_NoBakedLines)\n        return;\n\n    // This generates a triangular shape in the texture, with the various line widths stacked on top of each other to allow interpolation between them\n    ImFontAtlasCustomRect* r = atlas->GetCustomRectByIndex(atlas->PackIdLines);\n    IM_ASSERT(r->IsPacked());\n    for (unsigned int n = 0; n < IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1; n++) // +1 because of the zero-width row\n    {\n        // Each line consists of at least two empty pixels at the ends, with a line of solid pixels in the middle\n        unsigned int y = n;\n        unsigned int line_width = n;\n        unsigned int pad_left = (r->Width - line_width) / 2;\n        unsigned int pad_right = r->Width - (pad_left + line_width);\n\n        // Write each slice\n        IM_ASSERT(pad_left + line_width + pad_right == r->Width && y < r->Height); // Make sure we're inside the texture bounds before we start writing pixels\n        if (atlas->TexPixelsAlpha8 != NULL)\n        {\n            unsigned char* write_ptr = &atlas->TexPixelsAlpha8[r->X + ((r->Y + y) * atlas->TexWidth)];\n            for (unsigned int i = 0; i < pad_left; i++)\n                *(write_ptr + i) = 0x00;\n\n            for (unsigned int i = 0; i < line_width; i++)\n                *(write_ptr + pad_left + i) = 0xFF;\n\n            for (unsigned int i = 0; i < pad_right; i++)\n                *(write_ptr + pad_left + line_width + i) = 0x00;\n        }\n        else\n        {\n            unsigned int* write_ptr = &atlas->TexPixelsRGBA32[r->X + ((r->Y + y) * atlas->TexWidth)];\n            for (unsigned int i = 0; i < pad_left; i++)\n                *(write_ptr + i) = IM_COL32(255, 255, 255, 0);\n\n            for (unsigned int i = 0; i < line_width; i++)\n                *(write_ptr + pad_left + i) = IM_COL32_WHITE;\n\n            for (unsigned int i = 0; i < pad_right; i++)\n                *(write_ptr + pad_left + line_width + i) = IM_COL32(255, 255, 255, 0);\n        }\n\n        // Calculate UVs for this line\n        ImVec2 uv0 = ImVec2((float)(r->X + pad_left - 1), (float)(r->Y + y)) * atlas->TexUvScale;\n        ImVec2 uv1 = ImVec2((float)(r->X + pad_left + line_width + 1), (float)(r->Y + y + 1)) * atlas->TexUvScale;\n        float half_v = (uv0.y + uv1.y) * 0.5f; // Calculate a constant V in the middle of the row to avoid sampling artifacts\n        atlas->TexUvLines[n] = ImVec4(uv0.x, half_v, uv1.x, half_v);\n    }\n}\n\n// Note: this is called / shared by both the stb_truetype and the FreeType builder\nvoid ImFontAtlasBuildInit(ImFontAtlas* atlas)\n{\n    // Round font size\n    // - We started rounding in 1.90 WIP (18991) as our layout system currently doesn't support non-rounded font size well yet.\n    // - Note that using io.FontGlobalScale or SetWindowFontScale(), with are legacy-ish, partially supported features, can still lead to unrounded sizes.\n    // - We may support it better later and remove this rounding.\n    for (ImFontConfig& cfg : atlas->ConfigData)\n       cfg.SizePixels = ImTrunc(cfg.SizePixels);\n\n    // Register texture region for mouse cursors or standard white pixels\n    if (atlas->PackIdMouseCursors < 0)\n    {\n        if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors))\n            atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_W * 2 + 1, FONT_ATLAS_DEFAULT_TEX_DATA_H);\n        else\n            atlas->PackIdMouseCursors = atlas->AddCustomRectRegular(2, 2);\n    }\n\n    // Register texture region for thick lines\n    // The +2 here is to give space for the end caps, whilst height +1 is to accommodate the fact we have a zero-width row\n    if (atlas->PackIdLines < 0)\n    {\n        if (!(atlas->Flags & ImFontAtlasFlags_NoBakedLines))\n            atlas->PackIdLines = atlas->AddCustomRectRegular(IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 2, IM_DRAWLIST_TEX_LINES_WIDTH_MAX + 1);\n    }\n}\n\n// This is called/shared by both the stb_truetype and the FreeType builder.\nvoid ImFontAtlasBuildFinish(ImFontAtlas* atlas)\n{\n    // Render into our custom data blocks\n    IM_ASSERT(atlas->TexPixelsAlpha8 != NULL || atlas->TexPixelsRGBA32 != NULL);\n    ImFontAtlasBuildRenderDefaultTexData(atlas);\n    ImFontAtlasBuildRenderLinesTexData(atlas);\n\n    // Register custom rectangle glyphs\n    for (int i = 0; i < atlas->CustomRects.Size; i++)\n    {\n        const ImFontAtlasCustomRect* r = &atlas->CustomRects[i];\n        if (r->Font == NULL || r->GlyphID == 0)\n            continue;\n\n        // Will ignore ImFontConfig settings: GlyphMinAdvanceX, GlyphMinAdvanceY, GlyphExtraSpacing, PixelSnapH\n        IM_ASSERT(r->Font->ContainerAtlas == atlas);\n        ImVec2 uv0, uv1;\n        atlas->CalcCustomRectUV(r, &uv0, &uv1);\n        r->Font->AddGlyph(NULL, (ImWchar)r->GlyphID, r->GlyphOffset.x, r->GlyphOffset.y, r->GlyphOffset.x + r->Width, r->GlyphOffset.y + r->Height, uv0.x, uv0.y, uv1.x, uv1.y, r->GlyphAdvanceX);\n    }\n\n    // Build all fonts lookup tables\n    for (ImFont* font : atlas->Fonts)\n        if (font->DirtyLookupTables)\n            font->BuildLookupTable();\n\n    atlas->TexReady = true;\n}\n\n// Retrieve list of range (2 int per range, values are inclusive)\nconst ImWchar*   ImFontAtlas::GetGlyphRangesDefault()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0,\n    };\n    return &ranges[0];\n}\n\nconst ImWchar*   ImFontAtlas::GetGlyphRangesGreek()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x0370, 0x03FF, // Greek and Coptic\n        0,\n    };\n    return &ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesKorean()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x3131, 0x3163, // Korean alphabets\n        0xAC00, 0xD7A3, // Korean characters\n        0xFFFD, 0xFFFD, // Invalid\n        0,\n    };\n    return &ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesChineseFull()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x2000, 0x206F, // General Punctuation\n        0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana\n        0x31F0, 0x31FF, // Katakana Phonetic Extensions\n        0xFF00, 0xFFEF, // Half-width characters\n        0xFFFD, 0xFFFD, // Invalid\n        0x4e00, 0x9FAF, // CJK Ideograms\n        0,\n    };\n    return &ranges[0];\n}\n\nstatic void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short* accumulative_offsets, int accumulative_offsets_count, ImWchar* out_ranges)\n{\n    for (int n = 0; n < accumulative_offsets_count; n++, out_ranges += 2)\n    {\n        out_ranges[0] = out_ranges[1] = (ImWchar)(base_codepoint + accumulative_offsets[n]);\n        base_codepoint += accumulative_offsets[n];\n    }\n    out_ranges[0] = 0;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] ImFontAtlas glyph ranges helpers\n//-------------------------------------------------------------------------\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon()\n{\n    // Store 2500 regularly used characters for Simplified Chinese.\n    // Sourced from https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8\n    // This table covers 97.97% of all characters used during the month in July, 1987.\n    // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.\n    // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.)\n    static const short accumulative_offsets_from_0x4E00[] =\n    {\n        0,1,2,4,1,1,1,1,2,1,3,2,1,2,2,1,1,1,1,1,5,2,1,2,3,3,3,2,2,4,1,1,1,2,1,5,2,3,1,2,1,2,1,1,2,1,1,2,2,1,4,1,1,1,1,5,10,1,2,19,2,1,2,1,2,1,2,1,2,\n        1,5,1,6,3,2,1,2,2,1,1,1,4,8,5,1,1,4,1,1,3,1,2,1,5,1,2,1,1,1,10,1,1,5,2,4,6,1,4,2,2,2,12,2,1,1,6,1,1,1,4,1,1,4,6,5,1,4,2,2,4,10,7,1,1,4,2,4,\n        2,1,4,3,6,10,12,5,7,2,14,2,9,1,1,6,7,10,4,7,13,1,5,4,8,4,1,1,2,28,5,6,1,1,5,2,5,20,2,2,9,8,11,2,9,17,1,8,6,8,27,4,6,9,20,11,27,6,68,2,2,1,1,\n        1,2,1,2,2,7,6,11,3,3,1,1,3,1,2,1,1,1,1,1,3,1,1,8,3,4,1,5,7,2,1,4,4,8,4,2,1,2,1,1,4,5,6,3,6,2,12,3,1,3,9,2,4,3,4,1,5,3,3,1,3,7,1,5,1,1,1,1,2,\n        3,4,5,2,3,2,6,1,1,2,1,7,1,7,3,4,5,15,2,2,1,5,3,22,19,2,1,1,1,1,2,5,1,1,1,6,1,1,12,8,2,9,18,22,4,1,1,5,1,16,1,2,7,10,15,1,1,6,2,4,1,2,4,1,6,\n        1,1,3,2,4,1,6,4,5,1,2,1,1,2,1,10,3,1,3,2,1,9,3,2,5,7,2,19,4,3,6,1,1,1,1,1,4,3,2,1,1,1,2,5,3,1,1,1,2,2,1,1,2,1,1,2,1,3,1,1,1,3,7,1,4,1,1,2,1,\n        1,2,1,2,4,4,3,8,1,1,1,2,1,3,5,1,3,1,3,4,6,2,2,14,4,6,6,11,9,1,15,3,1,28,5,2,5,5,3,1,3,4,5,4,6,14,3,2,3,5,21,2,7,20,10,1,2,19,2,4,28,28,2,3,\n        2,1,14,4,1,26,28,42,12,40,3,52,79,5,14,17,3,2,2,11,3,4,6,3,1,8,2,23,4,5,8,10,4,2,7,3,5,1,1,6,3,1,2,2,2,5,28,1,1,7,7,20,5,3,29,3,17,26,1,8,4,\n        27,3,6,11,23,5,3,4,6,13,24,16,6,5,10,25,35,7,3,2,3,3,14,3,6,2,6,1,4,2,3,8,2,1,1,3,3,3,4,1,1,13,2,2,4,5,2,1,14,14,1,2,2,1,4,5,2,3,1,14,3,12,\n        3,17,2,16,5,1,2,1,8,9,3,19,4,2,2,4,17,25,21,20,28,75,1,10,29,103,4,1,2,1,1,4,2,4,1,2,3,24,2,2,2,1,1,2,1,3,8,1,1,1,2,1,1,3,1,1,1,6,1,5,3,1,1,\n        1,3,4,1,1,5,2,1,5,6,13,9,16,1,1,1,1,3,2,3,2,4,5,2,5,2,2,3,7,13,7,2,2,1,1,1,1,2,3,3,2,1,6,4,9,2,1,14,2,14,2,1,18,3,4,14,4,11,41,15,23,15,23,\n        176,1,3,4,1,1,1,1,5,3,1,2,3,7,3,1,1,2,1,2,4,4,6,2,4,1,9,7,1,10,5,8,16,29,1,1,2,2,3,1,3,5,2,4,5,4,1,1,2,2,3,3,7,1,6,10,1,17,1,44,4,6,2,1,1,6,\n        5,4,2,10,1,6,9,2,8,1,24,1,2,13,7,8,8,2,1,4,1,3,1,3,3,5,2,5,10,9,4,9,12,2,1,6,1,10,1,1,7,7,4,10,8,3,1,13,4,3,1,6,1,3,5,2,1,2,17,16,5,2,16,6,\n        1,4,2,1,3,3,6,8,5,11,11,1,3,3,2,4,6,10,9,5,7,4,7,4,7,1,1,4,2,1,3,6,8,7,1,6,11,5,5,3,24,9,4,2,7,13,5,1,8,82,16,61,1,1,1,4,2,2,16,10,3,8,1,1,\n        6,4,2,1,3,1,1,1,4,3,8,4,2,2,1,1,1,1,1,6,3,5,1,1,4,6,9,2,1,1,1,2,1,7,2,1,6,1,5,4,4,3,1,8,1,3,3,1,3,2,2,2,2,3,1,6,1,2,1,2,1,3,7,1,8,2,1,2,1,5,\n        2,5,3,5,10,1,2,1,1,3,2,5,11,3,9,3,5,1,1,5,9,1,2,1,5,7,9,9,8,1,3,3,3,6,8,2,3,2,1,1,32,6,1,2,15,9,3,7,13,1,3,10,13,2,14,1,13,10,2,1,3,10,4,15,\n        2,15,15,10,1,3,9,6,9,32,25,26,47,7,3,2,3,1,6,3,4,3,2,8,5,4,1,9,4,2,2,19,10,6,2,3,8,1,2,2,4,2,1,9,4,4,4,6,4,8,9,2,3,1,1,1,1,3,5,5,1,3,8,4,6,\n        2,1,4,12,1,5,3,7,13,2,5,8,1,6,1,2,5,14,6,1,5,2,4,8,15,5,1,23,6,62,2,10,1,1,8,1,2,2,10,4,2,2,9,2,1,1,3,2,3,1,5,3,3,2,1,3,8,1,1,1,11,3,1,1,4,\n        3,7,1,14,1,2,3,12,5,2,5,1,6,7,5,7,14,11,1,3,1,8,9,12,2,1,11,8,4,4,2,6,10,9,13,1,1,3,1,5,1,3,2,4,4,1,18,2,3,14,11,4,29,4,2,7,1,3,13,9,2,2,5,\n        3,5,20,7,16,8,5,72,34,6,4,22,12,12,28,45,36,9,7,39,9,191,1,1,1,4,11,8,4,9,2,3,22,1,1,1,1,4,17,1,7,7,1,11,31,10,2,4,8,2,3,2,1,4,2,16,4,32,2,\n        3,19,13,4,9,1,5,2,14,8,1,1,3,6,19,6,5,1,16,6,2,10,8,5,1,2,3,1,5,5,1,11,6,6,1,3,3,2,6,3,8,1,1,4,10,7,5,7,7,5,8,9,2,1,3,4,1,1,3,1,3,3,2,6,16,\n        1,4,6,3,1,10,6,1,3,15,2,9,2,10,25,13,9,16,6,2,2,10,11,4,3,9,1,2,6,6,5,4,30,40,1,10,7,12,14,33,6,3,6,7,3,1,3,1,11,14,4,9,5,12,11,49,18,51,31,\n        140,31,2,2,1,5,1,8,1,10,1,4,4,3,24,1,10,1,3,6,6,16,3,4,5,2,1,4,2,57,10,6,22,2,22,3,7,22,6,10,11,36,18,16,33,36,2,5,5,1,1,1,4,10,1,4,13,2,7,\n        5,2,9,3,4,1,7,43,3,7,3,9,14,7,9,1,11,1,1,3,7,4,18,13,1,14,1,3,6,10,73,2,2,30,6,1,11,18,19,13,22,3,46,42,37,89,7,3,16,34,2,2,3,9,1,7,1,1,1,2,\n        2,4,10,7,3,10,3,9,5,28,9,2,6,13,7,3,1,3,10,2,7,2,11,3,6,21,54,85,2,1,4,2,2,1,39,3,21,2,2,5,1,1,1,4,1,1,3,4,15,1,3,2,4,4,2,3,8,2,20,1,8,7,13,\n        4,1,26,6,2,9,34,4,21,52,10,4,4,1,5,12,2,11,1,7,2,30,12,44,2,30,1,1,3,6,16,9,17,39,82,2,2,24,7,1,7,3,16,9,14,44,2,1,2,1,2,3,5,2,4,1,6,7,5,3,\n        2,6,1,11,5,11,2,1,18,19,8,1,3,24,29,2,1,3,5,2,2,1,13,6,5,1,46,11,3,5,1,1,5,8,2,10,6,12,6,3,7,11,2,4,16,13,2,5,1,1,2,2,5,2,28,5,2,23,10,8,4,\n        4,22,39,95,38,8,14,9,5,1,13,5,4,3,13,12,11,1,9,1,27,37,2,5,4,4,63,211,95,2,2,2,1,3,5,2,1,1,2,2,1,1,1,3,2,4,1,2,1,1,5,2,2,1,1,2,3,1,3,1,1,1,\n        3,1,4,2,1,3,6,1,1,3,7,15,5,3,2,5,3,9,11,4,2,22,1,6,3,8,7,1,4,28,4,16,3,3,25,4,4,27,27,1,4,1,2,2,7,1,3,5,2,28,8,2,14,1,8,6,16,25,3,3,3,14,3,\n        3,1,1,2,1,4,6,3,8,4,1,1,1,2,3,6,10,6,2,3,18,3,2,5,5,4,3,1,5,2,5,4,23,7,6,12,6,4,17,11,9,5,1,1,10,5,12,1,1,11,26,33,7,3,6,1,17,7,1,5,12,1,11,\n        2,4,1,8,14,17,23,1,2,1,7,8,16,11,9,6,5,2,6,4,16,2,8,14,1,11,8,9,1,1,1,9,25,4,11,19,7,2,15,2,12,8,52,7,5,19,2,16,4,36,8,1,16,8,24,26,4,6,2,9,\n        5,4,36,3,28,12,25,15,37,27,17,12,59,38,5,32,127,1,2,9,17,14,4,1,2,1,1,8,11,50,4,14,2,19,16,4,17,5,4,5,26,12,45,2,23,45,104,30,12,8,3,10,2,2,\n        3,3,1,4,20,7,2,9,6,15,2,20,1,3,16,4,11,15,6,134,2,5,59,1,2,2,2,1,9,17,3,26,137,10,211,59,1,2,4,1,4,1,1,1,2,6,2,3,1,1,2,3,2,3,1,3,4,4,2,3,3,\n        1,4,3,1,7,2,2,3,1,2,1,3,3,3,2,2,3,2,1,3,14,6,1,3,2,9,6,15,27,9,34,145,1,1,2,1,1,1,1,2,1,1,1,1,2,2,2,3,1,2,1,1,1,2,3,5,8,3,5,2,4,1,3,2,2,2,12,\n        4,1,1,1,10,4,5,1,20,4,16,1,15,9,5,12,2,9,2,5,4,2,26,19,7,1,26,4,30,12,15,42,1,6,8,172,1,1,4,2,1,1,11,2,2,4,2,1,2,1,10,8,1,2,1,4,5,1,2,5,1,8,\n        4,1,3,4,2,1,6,2,1,3,4,1,2,1,1,1,1,12,5,7,2,4,3,1,1,1,3,3,6,1,2,2,3,3,3,2,1,2,12,14,11,6,6,4,12,2,8,1,7,10,1,35,7,4,13,15,4,3,23,21,28,52,5,\n        26,5,6,1,7,10,2,7,53,3,2,1,1,1,2,163,532,1,10,11,1,3,3,4,8,2,8,6,2,2,23,22,4,2,2,4,2,1,3,1,3,3,5,9,8,2,1,2,8,1,10,2,12,21,20,15,105,2,3,1,1,\n        3,2,3,1,1,2,5,1,4,15,11,19,1,1,1,1,5,4,5,1,1,2,5,3,5,12,1,2,5,1,11,1,1,15,9,1,4,5,3,26,8,2,1,3,1,1,15,19,2,12,1,2,5,2,7,2,19,2,20,6,26,7,5,\n        2,2,7,34,21,13,70,2,128,1,1,2,1,1,2,1,1,3,2,2,2,15,1,4,1,3,4,42,10,6,1,49,85,8,1,2,1,1,4,4,2,3,6,1,5,7,4,3,211,4,1,2,1,2,5,1,2,4,2,2,6,5,6,\n        10,3,4,48,100,6,2,16,296,5,27,387,2,2,3,7,16,8,5,38,15,39,21,9,10,3,7,59,13,27,21,47,5,21,6\n    };\n    static ImWchar base_ranges[] = // not zero-terminated\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x2000, 0x206F, // General Punctuation\n        0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana\n        0x31F0, 0x31FF, // Katakana Phonetic Extensions\n        0xFF00, 0xFFEF, // Half-width characters\n        0xFFFD, 0xFFFD  // Invalid\n    };\n    static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 };\n    if (!full_ranges[0])\n    {\n        memcpy(full_ranges, base_ranges, sizeof(base_ranges));\n        UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges));\n    }\n    return &full_ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesJapanese()\n{\n    // 2999 ideograms code points for Japanese\n    // - 2136 Joyo (meaning \"for regular use\" or \"for common use\") Kanji code points\n    // - 863 Jinmeiyo (meaning \"for personal name\") Kanji code points\n    // - Sourced from official information provided by the government agencies of Japan:\n    //   - List of Joyo Kanji by the Agency for Cultural Affairs\n    //     - https://www.bunka.go.jp/kokugo_nihongo/sisaku/joho/joho/kijun/naikaku/kanji/\n    //   - List of Jinmeiyo Kanji by the Ministry of Justice\n    //     - http://www.moj.go.jp/MINJI/minji86.html\n    //   - Available under the terms of the Creative Commons Attribution 4.0 International (CC BY 4.0).\n    //     - https://creativecommons.org/licenses/by/4.0/legalcode\n    // - You can generate this code by the script at:\n    //   - https://github.com/vaiorabbit/everyday_use_kanji\n    // - References:\n    //   - List of Joyo Kanji\n    //     - (Wikipedia) https://en.wikipedia.org/wiki/List_of_j%C5%8Dy%C5%8D_kanji\n    //   - List of Jinmeiyo Kanji\n    //     - (Wikipedia) https://en.wikipedia.org/wiki/Jinmeiy%C5%8D_kanji\n    // - Missing 1 Joyo Kanji: U+20B9F (Kun'yomi: Shikaru, On'yomi: Shitsu,shichi), see https://github.com/ocornut/imgui/pull/3627 for details.\n    // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.\n    // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.)\n    static const short accumulative_offsets_from_0x4E00[] =\n    {\n        0,1,2,4,1,1,1,1,2,1,3,3,2,2,1,5,3,5,7,5,6,1,2,1,7,2,6,3,1,8,1,1,4,1,1,18,2,11,2,6,2,1,2,1,5,1,2,1,3,1,2,1,2,3,3,1,1,2,3,1,1,1,12,7,9,1,4,5,1,\n        1,2,1,10,1,1,9,2,2,4,5,6,9,3,1,1,1,1,9,3,18,5,2,2,2,2,1,6,3,7,1,1,1,1,2,2,4,2,1,23,2,10,4,3,5,2,4,10,2,4,13,1,6,1,9,3,1,1,6,6,7,6,3,1,2,11,3,\n        2,2,3,2,15,2,2,5,4,3,6,4,1,2,5,2,12,16,6,13,9,13,2,1,1,7,16,4,7,1,19,1,5,1,2,2,7,7,8,2,6,5,4,9,18,7,4,5,9,13,11,8,15,2,1,1,1,2,1,2,2,1,2,2,8,\n        2,9,3,3,1,1,4,4,1,1,1,4,9,1,4,3,5,5,2,7,5,3,4,8,2,1,13,2,3,3,1,14,1,1,4,5,1,3,6,1,5,2,1,1,3,3,3,3,1,1,2,7,6,6,7,1,4,7,6,1,1,1,1,1,12,3,3,9,5,\n        2,6,1,5,6,1,2,3,18,2,4,14,4,1,3,6,1,1,6,3,5,5,3,2,2,2,2,12,3,1,4,2,3,2,3,11,1,7,4,1,2,1,3,17,1,9,1,24,1,1,4,2,2,4,1,2,7,1,1,1,3,1,2,2,4,15,1,\n        1,2,1,1,2,1,5,2,5,20,2,5,9,1,10,8,7,6,1,1,1,1,1,1,6,2,1,2,8,1,1,1,1,5,1,1,3,1,1,1,1,3,1,1,12,4,1,3,1,1,1,1,1,10,3,1,7,5,13,1,2,3,4,6,1,1,30,\n        2,9,9,1,15,38,11,3,1,8,24,7,1,9,8,10,2,1,9,31,2,13,6,2,9,4,49,5,2,15,2,1,10,2,1,1,1,2,2,6,15,30,35,3,14,18,8,1,16,10,28,12,19,45,38,1,3,2,3,\n        13,2,1,7,3,6,5,3,4,3,1,5,7,8,1,5,3,18,5,3,6,1,21,4,24,9,24,40,3,14,3,21,3,2,1,2,4,2,3,1,15,15,6,5,1,1,3,1,5,6,1,9,7,3,3,2,1,4,3,8,21,5,16,4,\n        5,2,10,11,11,3,6,3,2,9,3,6,13,1,2,1,1,1,1,11,12,6,6,1,4,2,6,5,2,1,1,3,3,6,13,3,1,1,5,1,2,3,3,14,2,1,2,2,2,5,1,9,5,1,1,6,12,3,12,3,4,13,2,14,\n        2,8,1,17,5,1,16,4,2,2,21,8,9,6,23,20,12,25,19,9,38,8,3,21,40,25,33,13,4,3,1,4,1,2,4,1,2,5,26,2,1,1,2,1,3,6,2,1,1,1,1,1,1,2,3,1,1,1,9,2,3,1,1,\n        1,3,6,3,2,1,1,6,6,1,8,2,2,2,1,4,1,2,3,2,7,3,2,4,1,2,1,2,2,1,1,1,1,1,3,1,2,5,4,10,9,4,9,1,1,1,1,1,1,5,3,2,1,6,4,9,6,1,10,2,31,17,8,3,7,5,40,1,\n        7,7,1,6,5,2,10,7,8,4,15,39,25,6,28,47,18,10,7,1,3,1,1,2,1,1,1,3,3,3,1,1,1,3,4,2,1,4,1,3,6,10,7,8,6,2,2,1,3,3,2,5,8,7,9,12,2,15,1,1,4,1,2,1,1,\n        1,3,2,1,3,3,5,6,2,3,2,10,1,4,2,8,1,1,1,11,6,1,21,4,16,3,1,3,1,4,2,3,6,5,1,3,1,1,3,3,4,6,1,1,10,4,2,7,10,4,7,4,2,9,4,3,1,1,1,4,1,8,3,4,1,3,1,\n        6,1,4,2,1,4,7,2,1,8,1,4,5,1,1,2,2,4,6,2,7,1,10,1,1,3,4,11,10,8,21,4,6,1,3,5,2,1,2,28,5,5,2,3,13,1,2,3,1,4,2,1,5,20,3,8,11,1,3,3,3,1,8,10,9,2,\n        10,9,2,3,1,1,2,4,1,8,3,6,1,7,8,6,11,1,4,29,8,4,3,1,2,7,13,1,4,1,6,2,6,12,12,2,20,3,2,3,6,4,8,9,2,7,34,5,1,18,6,1,1,4,4,5,7,9,1,2,2,4,3,4,1,7,\n        2,2,2,6,2,3,25,5,3,6,1,4,6,7,4,2,1,4,2,13,6,4,4,3,1,5,3,4,4,3,2,1,1,4,1,2,1,1,3,1,11,1,6,3,1,7,3,6,2,8,8,6,9,3,4,11,3,2,10,12,2,5,11,1,6,4,5,\n        3,1,8,5,4,6,6,3,5,1,1,3,2,1,2,2,6,17,12,1,10,1,6,12,1,6,6,19,9,6,16,1,13,4,4,15,7,17,6,11,9,15,12,6,7,2,1,2,2,15,9,3,21,4,6,49,18,7,3,2,3,1,\n        6,8,2,2,6,2,9,1,3,6,4,4,1,2,16,2,5,2,1,6,2,3,5,3,1,2,5,1,2,1,9,3,1,8,6,4,8,11,3,1,1,1,1,3,1,13,8,4,1,3,2,2,1,4,1,11,1,5,2,1,5,2,5,8,6,1,1,7,\n        4,3,8,3,2,7,2,1,5,1,5,2,4,7,6,2,8,5,1,11,4,5,3,6,18,1,2,13,3,3,1,21,1,1,4,1,4,1,1,1,8,1,2,2,7,1,2,4,2,2,9,2,1,1,1,4,3,6,3,12,5,1,1,1,5,6,3,2,\n        4,8,2,2,4,2,7,1,8,9,5,2,3,2,1,3,2,13,7,14,6,5,1,1,2,1,4,2,23,2,1,1,6,3,1,4,1,15,3,1,7,3,9,14,1,3,1,4,1,1,5,8,1,3,8,3,8,15,11,4,14,4,4,2,5,5,\n        1,7,1,6,14,7,7,8,5,15,4,8,6,5,6,2,1,13,1,20,15,11,9,2,5,6,2,11,2,6,2,5,1,5,8,4,13,19,25,4,1,1,11,1,34,2,5,9,14,6,2,2,6,1,1,14,1,3,14,13,1,6,\n        12,21,14,14,6,32,17,8,32,9,28,1,2,4,11,8,3,1,14,2,5,15,1,1,1,1,3,6,4,1,3,4,11,3,1,1,11,30,1,5,1,4,1,5,8,1,1,3,2,4,3,17,35,2,6,12,17,3,1,6,2,\n        1,1,12,2,7,3,3,2,1,16,2,8,3,6,5,4,7,3,3,8,1,9,8,5,1,2,1,3,2,8,1,2,9,12,1,1,2,3,8,3,24,12,4,3,7,5,8,3,3,3,3,3,3,1,23,10,3,1,2,2,6,3,1,16,1,16,\n        22,3,10,4,11,6,9,7,7,3,6,2,2,2,4,10,2,1,1,2,8,7,1,6,4,1,3,3,3,5,10,12,12,2,3,12,8,15,1,1,16,6,6,1,5,9,11,4,11,4,2,6,12,1,17,5,13,1,4,9,5,1,11,\n        2,1,8,1,5,7,28,8,3,5,10,2,17,3,38,22,1,2,18,12,10,4,38,18,1,4,44,19,4,1,8,4,1,12,1,4,31,12,1,14,7,75,7,5,10,6,6,13,3,2,11,11,3,2,5,28,15,6,18,\n        18,5,6,4,3,16,1,7,18,7,36,3,5,3,1,7,1,9,1,10,7,2,4,2,6,2,9,7,4,3,32,12,3,7,10,2,23,16,3,1,12,3,31,4,11,1,3,8,9,5,1,30,15,6,12,3,2,2,11,19,9,\n        14,2,6,2,3,19,13,17,5,3,3,25,3,14,1,1,1,36,1,3,2,19,3,13,36,9,13,31,6,4,16,34,2,5,4,2,3,3,5,1,1,1,4,3,1,17,3,2,3,5,3,1,3,2,3,5,6,3,12,11,1,3,\n        1,2,26,7,12,7,2,14,3,3,7,7,11,25,25,28,16,4,36,1,2,1,6,2,1,9,3,27,17,4,3,4,13,4,1,3,2,2,1,10,4,2,4,6,3,8,2,1,18,1,1,24,2,2,4,33,2,3,63,7,1,6,\n        40,7,3,4,4,2,4,15,18,1,16,1,1,11,2,41,14,1,3,18,13,3,2,4,16,2,17,7,15,24,7,18,13,44,2,2,3,6,1,1,7,5,1,7,1,4,3,3,5,10,8,2,3,1,8,1,1,27,4,2,1,\n        12,1,2,1,10,6,1,6,7,5,2,3,7,11,5,11,3,6,6,2,3,15,4,9,1,1,2,1,2,11,2,8,12,8,5,4,2,3,1,5,2,2,1,14,1,12,11,4,1,11,17,17,4,3,2,5,5,7,3,1,5,9,9,8,\n        2,5,6,6,13,13,2,1,2,6,1,2,2,49,4,9,1,2,10,16,7,8,4,3,2,23,4,58,3,29,1,14,19,19,11,11,2,7,5,1,3,4,6,2,18,5,12,12,17,17,3,3,2,4,1,6,2,3,4,3,1,\n        1,1,1,5,1,1,9,1,3,1,3,6,1,8,1,1,2,6,4,14,3,1,4,11,4,1,3,32,1,2,4,13,4,1,2,4,2,1,3,1,11,1,4,2,1,4,4,6,3,5,1,6,5,7,6,3,23,3,5,3,5,3,3,13,3,9,10,\n        1,12,10,2,3,18,13,7,160,52,4,2,2,3,2,14,5,4,12,4,6,4,1,20,4,11,6,2,12,27,1,4,1,2,2,7,4,5,2,28,3,7,25,8,3,19,3,6,10,2,2,1,10,2,5,4,1,3,4,1,5,\n        3,2,6,9,3,6,2,16,3,3,16,4,5,5,3,2,1,2,16,15,8,2,6,21,2,4,1,22,5,8,1,1,21,11,2,1,11,11,19,13,12,4,2,3,2,3,6,1,8,11,1,4,2,9,5,2,1,11,2,9,1,1,2,\n        14,31,9,3,4,21,14,4,8,1,7,2,2,2,5,1,4,20,3,3,4,10,1,11,9,8,2,1,4,5,14,12,14,2,17,9,6,31,4,14,1,20,13,26,5,2,7,3,6,13,2,4,2,19,6,2,2,18,9,3,5,\n        12,12,14,4,6,2,3,6,9,5,22,4,5,25,6,4,8,5,2,6,27,2,35,2,16,3,7,8,8,6,6,5,9,17,2,20,6,19,2,13,3,1,1,1,4,17,12,2,14,7,1,4,18,12,38,33,2,10,1,1,\n        2,13,14,17,11,50,6,33,20,26,74,16,23,45,50,13,38,33,6,6,7,4,4,2,1,3,2,5,8,7,8,9,3,11,21,9,13,1,3,10,6,7,1,2,2,18,5,5,1,9,9,2,68,9,19,13,2,5,\n        1,4,4,7,4,13,3,9,10,21,17,3,26,2,1,5,2,4,5,4,1,7,4,7,3,4,2,1,6,1,1,20,4,1,9,2,2,1,3,3,2,3,2,1,1,1,20,2,3,1,6,2,3,6,2,4,8,1,3,2,10,3,5,3,4,4,\n        3,4,16,1,6,1,10,2,4,2,1,1,2,10,11,2,2,3,1,24,31,4,10,10,2,5,12,16,164,15,4,16,7,9,15,19,17,1,2,1,1,5,1,1,1,1,1,3,1,4,3,1,3,1,3,1,2,1,1,3,3,7,\n        2,8,1,2,2,2,1,3,4,3,7,8,12,92,2,10,3,1,3,14,5,25,16,42,4,7,7,4,2,21,5,27,26,27,21,25,30,31,2,1,5,13,3,22,5,6,6,11,9,12,1,5,9,7,5,5,22,60,3,5,\n        13,1,1,8,1,1,3,3,2,1,9,3,3,18,4,1,2,3,7,6,3,1,2,3,9,1,3,1,3,2,1,3,1,1,1,2,1,11,3,1,6,9,1,3,2,3,1,2,1,5,1,1,4,3,4,1,2,2,4,4,1,7,2,1,2,2,3,5,13,\n        18,3,4,14,9,9,4,16,3,7,5,8,2,6,48,28,3,1,1,4,2,14,8,2,9,2,1,15,2,4,3,2,10,16,12,8,7,1,1,3,1,1,1,2,7,4,1,6,4,38,39,16,23,7,15,15,3,2,12,7,21,\n        37,27,6,5,4,8,2,10,8,8,6,5,1,2,1,3,24,1,16,17,9,23,10,17,6,1,51,55,44,13,294,9,3,6,2,4,2,2,15,1,1,1,13,21,17,68,14,8,9,4,1,4,9,3,11,7,1,1,1,\n        5,6,3,2,1,1,1,2,3,8,1,2,2,4,1,5,5,2,1,4,3,7,13,4,1,4,1,3,1,1,1,5,5,10,1,6,1,5,2,1,5,2,4,1,4,5,7,3,18,2,9,11,32,4,3,3,2,4,7,11,16,9,11,8,13,38,\n        32,8,4,2,1,1,2,1,2,4,4,1,1,1,4,1,21,3,11,1,16,1,1,6,1,3,2,4,9,8,57,7,44,1,3,3,13,3,10,1,1,7,5,2,7,21,47,63,3,15,4,7,1,16,1,1,2,8,2,3,42,15,4,\n        1,29,7,22,10,3,78,16,12,20,18,4,67,11,5,1,3,15,6,21,31,32,27,18,13,71,35,5,142,4,10,1,2,50,19,33,16,35,37,16,19,27,7,1,133,19,1,4,8,7,20,1,4,\n        4,1,10,3,1,6,1,2,51,5,40,15,24,43,22928,11,1,13,154,70,3,1,1,7,4,10,1,2,1,1,2,1,2,1,2,2,1,1,2,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,\n        3,2,1,1,1,1,2,1,1,\n    };\n    static ImWchar base_ranges[] = // not zero-terminated\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana\n        0x31F0, 0x31FF, // Katakana Phonetic Extensions\n        0xFF00, 0xFFEF, // Half-width characters\n        0xFFFD, 0xFFFD  // Invalid\n    };\n    static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 };\n    if (!full_ranges[0])\n    {\n        memcpy(full_ranges, base_ranges, sizeof(base_ranges));\n        UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges));\n    }\n    return &full_ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesCyrillic()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin + Latin Supplement\n        0x0400, 0x052F, // Cyrillic + Cyrillic Supplement\n        0x2DE0, 0x2DFF, // Cyrillic Extended-A\n        0xA640, 0xA69F, // Cyrillic Extended-B\n        0,\n    };\n    return &ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesThai()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin\n        0x2010, 0x205E, // Punctuations\n        0x0E00, 0x0E7F, // Thai\n        0,\n    };\n    return &ranges[0];\n}\n\nconst ImWchar*  ImFontAtlas::GetGlyphRangesVietnamese()\n{\n    static const ImWchar ranges[] =\n    {\n        0x0020, 0x00FF, // Basic Latin\n        0x0102, 0x0103,\n        0x0110, 0x0111,\n        0x0128, 0x0129,\n        0x0168, 0x0169,\n        0x01A0, 0x01A1,\n        0x01AF, 0x01B0,\n        0x1EA0, 0x1EF9,\n        0,\n    };\n    return &ranges[0];\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFontGlyphRangesBuilder\n//-----------------------------------------------------------------------------\n\nvoid ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end)\n{\n    while (text_end ? (text < text_end) : *text)\n    {\n        unsigned int c = 0;\n        int c_len = ImTextCharFromUtf8(&c, text, text_end);\n        text += c_len;\n        if (c_len == 0)\n            break;\n        AddChar((ImWchar)c);\n    }\n}\n\nvoid ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges)\n{\n    for (; ranges[0]; ranges += 2)\n        for (unsigned int c = ranges[0]; c <= ranges[1] && c <= IM_UNICODE_CODEPOINT_MAX; c++) //-V560\n            AddChar((ImWchar)c);\n}\n\nvoid ImFontGlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)\n{\n    const int max_codepoint = IM_UNICODE_CODEPOINT_MAX;\n    for (int n = 0; n <= max_codepoint; n++)\n        if (GetBit(n))\n        {\n            out_ranges->push_back((ImWchar)n);\n            while (n < max_codepoint && GetBit(n + 1))\n                n++;\n            out_ranges->push_back((ImWchar)n);\n        }\n    out_ranges->push_back(0);\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFont\n//-----------------------------------------------------------------------------\n\nImFont::ImFont()\n{\n    FontSize = 0.0f;\n    FallbackAdvanceX = 0.0f;\n    FallbackChar = (ImWchar)-1;\n    EllipsisChar = (ImWchar)-1;\n    EllipsisWidth = EllipsisCharStep = 0.0f;\n    EllipsisCharCount = 0;\n    FallbackGlyph = NULL;\n    ContainerAtlas = NULL;\n    ConfigData = NULL;\n    ConfigDataCount = 0;\n    DirtyLookupTables = false;\n    Scale = 1.0f;\n    Ascent = Descent = 0.0f;\n    MetricsTotalSurface = 0;\n    memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap));\n}\n\nImFont::~ImFont()\n{\n    ClearOutputData();\n}\n\nvoid    ImFont::ClearOutputData()\n{\n    FontSize = 0.0f;\n    FallbackAdvanceX = 0.0f;\n    Glyphs.clear();\n    IndexAdvanceX.clear();\n    IndexLookup.clear();\n    FallbackGlyph = NULL;\n    ContainerAtlas = NULL;\n    DirtyLookupTables = true;\n    Ascent = Descent = 0.0f;\n    MetricsTotalSurface = 0;\n}\n\nstatic ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count)\n{\n    for (int n = 0; n < candidate_chars_count; n++)\n        if (font->FindGlyphNoFallback(candidate_chars[n]) != NULL)\n            return candidate_chars[n];\n    return (ImWchar)-1;\n}\n\nvoid ImFont::BuildLookupTable()\n{\n    int max_codepoint = 0;\n    for (int i = 0; i != Glyphs.Size; i++)\n        max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint);\n\n    // Build lookup table\n    IM_ASSERT(Glyphs.Size > 0 && \"Font has not loaded glyph!\");\n    IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved\n    IndexAdvanceX.clear();\n    IndexLookup.clear();\n    DirtyLookupTables = false;\n    memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap));\n    GrowIndex(max_codepoint + 1);\n    for (int i = 0; i < Glyphs.Size; i++)\n    {\n        int codepoint = (int)Glyphs[i].Codepoint;\n        IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX;\n        IndexLookup[codepoint] = (ImWchar)i;\n\n        // Mark 4K page as used\n        const int page_n = codepoint / 4096;\n        Used4kPagesMap[page_n >> 3] |= 1 << (page_n & 7);\n    }\n\n    // Create a glyph to handle TAB\n    // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at \"column 0\" ?)\n    if (FindGlyph((ImWchar)' '))\n    {\n        if (Glyphs.back().Codepoint != '\\t')   // So we can call this function multiple times (FIXME: Flaky)\n            Glyphs.resize(Glyphs.Size + 1);\n        ImFontGlyph& tab_glyph = Glyphs.back();\n        tab_glyph = *FindGlyph((ImWchar)' ');\n        tab_glyph.Codepoint = '\\t';\n        tab_glyph.AdvanceX *= IM_TABSIZE;\n        IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX;\n        IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size - 1);\n    }\n\n    // Mark special glyphs as not visible (note that AddGlyph already mark as non-visible glyphs with zero-size polygons)\n    SetGlyphVisible((ImWchar)' ', false);\n    SetGlyphVisible((ImWchar)'\\t', false);\n\n    // Setup Fallback character\n    const ImWchar fallback_chars[] = { (ImWchar)IM_UNICODE_CODEPOINT_INVALID, (ImWchar)'?', (ImWchar)' ' };\n    FallbackGlyph = FindGlyphNoFallback(FallbackChar);\n    if (FallbackGlyph == NULL)\n    {\n        FallbackChar = FindFirstExistingGlyph(this, fallback_chars, IM_ARRAYSIZE(fallback_chars));\n        FallbackGlyph = FindGlyphNoFallback(FallbackChar);\n        if (FallbackGlyph == NULL)\n        {\n            FallbackGlyph = &Glyphs.back();\n            FallbackChar = (ImWchar)FallbackGlyph->Codepoint;\n        }\n    }\n    FallbackAdvanceX = FallbackGlyph->AdvanceX;\n    for (int i = 0; i < max_codepoint + 1; i++)\n        if (IndexAdvanceX[i] < 0.0f)\n            IndexAdvanceX[i] = FallbackAdvanceX;\n\n    // Setup Ellipsis character. It is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).\n    // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.\n    // FIXME: Note that 0x2026 is rarely included in our font ranges. Because of this we are more likely to use three individual dots.\n    const ImWchar ellipsis_chars[] = { (ImWchar)0x2026, (ImWchar)0x0085 };\n    const ImWchar dots_chars[] = { (ImWchar)'.', (ImWchar)0xFF0E };\n    if (EllipsisChar == (ImWchar)-1)\n        EllipsisChar = FindFirstExistingGlyph(this, ellipsis_chars, IM_ARRAYSIZE(ellipsis_chars));\n    const ImWchar dot_char = FindFirstExistingGlyph(this, dots_chars, IM_ARRAYSIZE(dots_chars));\n    if (EllipsisChar != (ImWchar)-1)\n    {\n        EllipsisCharCount = 1;\n        EllipsisWidth = EllipsisCharStep = FindGlyph(EllipsisChar)->X1;\n    }\n    else if (dot_char != (ImWchar)-1)\n    {\n        const ImFontGlyph* glyph = FindGlyph(dot_char);\n        EllipsisChar = dot_char;\n        EllipsisCharCount = 3;\n        EllipsisCharStep = (glyph->X1 - glyph->X0) + 1.0f;\n        EllipsisWidth = EllipsisCharStep * 3.0f - 1.0f;\n    }\n}\n\n// API is designed this way to avoid exposing the 4K page size\n// e.g. use with IsGlyphRangeUnused(0, 255)\nbool ImFont::IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last)\n{\n    unsigned int page_begin = (c_begin / 4096);\n    unsigned int page_last = (c_last / 4096);\n    for (unsigned int page_n = page_begin; page_n <= page_last; page_n++)\n        if ((page_n >> 3) < sizeof(Used4kPagesMap))\n            if (Used4kPagesMap[page_n >> 3] & (1 << (page_n & 7)))\n                return false;\n    return true;\n}\n\nvoid ImFont::SetGlyphVisible(ImWchar c, bool visible)\n{\n    if (ImFontGlyph* glyph = (ImFontGlyph*)(void*)FindGlyph((ImWchar)c))\n        glyph->Visible = visible ? 1 : 0;\n}\n\nvoid ImFont::GrowIndex(int new_size)\n{\n    IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size);\n    if (new_size <= IndexLookup.Size)\n        return;\n    IndexAdvanceX.resize(new_size, -1.0f);\n    IndexLookup.resize(new_size, (ImWchar)-1);\n}\n\n// x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero.\n// Not to be mistaken with texture coordinates, which are held by u0/v0/u1/v1 in normalized format (0.0..1.0 on each texture axis).\n// 'cfg' is not necessarily == 'this->ConfigData' because multiple source fonts+configs can be used to build one target font.\nvoid ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x)\n{\n    if (cfg != NULL)\n    {\n        // Clamp & recenter if needed\n        const float advance_x_original = advance_x;\n        advance_x = ImClamp(advance_x, cfg->GlyphMinAdvanceX, cfg->GlyphMaxAdvanceX);\n        if (advance_x != advance_x_original)\n        {\n            float char_off_x = cfg->PixelSnapH ? ImTrunc((advance_x - advance_x_original) * 0.5f) : (advance_x - advance_x_original) * 0.5f;\n            x0 += char_off_x;\n            x1 += char_off_x;\n        }\n\n        // Snap to pixel\n        if (cfg->PixelSnapH)\n            advance_x = IM_ROUND(advance_x);\n\n        // Bake spacing\n        advance_x += cfg->GlyphExtraSpacing.x;\n    }\n\n    Glyphs.resize(Glyphs.Size + 1);\n    ImFontGlyph& glyph = Glyphs.back();\n    glyph.Codepoint = (unsigned int)codepoint;\n    glyph.Visible = (x0 != x1) && (y0 != y1);\n    glyph.Colored = false;\n    glyph.X0 = x0;\n    glyph.Y0 = y0;\n    glyph.X1 = x1;\n    glyph.Y1 = y1;\n    glyph.U0 = u0;\n    glyph.V0 = v0;\n    glyph.U1 = u1;\n    glyph.V1 = v1;\n    glyph.AdvanceX = advance_x;\n\n    // Compute rough surface usage metrics (+1 to account for average padding, +0.99 to round)\n    // We use (U1-U0)*TexWidth instead of X1-X0 to account for oversampling.\n    float pad = ContainerAtlas->TexGlyphPadding + 0.99f;\n    DirtyLookupTables = true;\n    MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * ContainerAtlas->TexWidth + pad) * (int)((glyph.V1 - glyph.V0) * ContainerAtlas->TexHeight + pad);\n}\n\nvoid ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst)\n{\n    IM_ASSERT(IndexLookup.Size > 0);    // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function.\n    unsigned int index_size = (unsigned int)IndexLookup.Size;\n\n    if (dst < index_size && IndexLookup.Data[dst] == (ImWchar)-1 && !overwrite_dst) // 'dst' already exists\n        return;\n    if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op\n        return;\n\n    GrowIndex(dst + 1);\n    IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (ImWchar)-1;\n    IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f;\n}\n\nconst ImFontGlyph* ImFont::FindGlyph(ImWchar c) const\n{\n    if (c >= (size_t)IndexLookup.Size)\n        return FallbackGlyph;\n    const ImWchar i = IndexLookup.Data[c];\n    if (i == (ImWchar)-1)\n        return FallbackGlyph;\n    return &Glyphs.Data[i];\n}\n\nconst ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const\n{\n    if (c >= (size_t)IndexLookup.Size)\n        return NULL;\n    const ImWchar i = IndexLookup.Data[c];\n    if (i == (ImWchar)-1)\n        return NULL;\n    return &Glyphs.Data[i];\n}\n\n// Wrapping skips upcoming blanks\nstatic inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end)\n{\n    while (text < text_end && ImCharIsBlankA(*text))\n        text++;\n    if (*text == '\\n')\n        text++;\n    return text;\n}\n\n// Simple word-wrapping for English, not full-featured. Please submit failing cases!\n// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.\n// FIXME: Much possible improvements (don't cut things like \"word !\", \"word!!!\" but cut within \"word,,,,\", more sensible support for punctuations, support for Unicode punctuations, etc.)\nconst char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const\n{\n    // For references, possible wrap point marked with ^\n    //  \"aaa bbb, ccc,ddd. eee   fff. ggg!\"\n    //      ^    ^    ^   ^   ^__    ^    ^\n\n    // List of hardcoded separators: .,;!?'\"\n\n    // Skip extra blanks after a line returns (that includes not counting them in width computation)\n    // e.g. \"Hello    world\" --> \"Hello\" \"World\"\n\n    // Cut words that cannot possibly fit within one line.\n    // e.g.: \"The tropical fish\" with ~5 characters worth of width --> \"The tr\" \"opical\" \"fish\"\n    float line_width = 0.0f;\n    float word_width = 0.0f;\n    float blank_width = 0.0f;\n    wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters\n\n    const char* word_end = text;\n    const char* prev_word_end = NULL;\n    bool inside_word = true;\n\n    const char* s = text;\n    IM_ASSERT(text_end != NULL);\n    while (s < text_end)\n    {\n        unsigned int c = (unsigned int)*s;\n        const char* next_s;\n        if (c < 0x80)\n            next_s = s + 1;\n        else\n            next_s = s + ImTextCharFromUtf8(&c, s, text_end);\n\n        if (c < 32)\n        {\n            if (c == '\\n')\n            {\n                line_width = word_width = blank_width = 0.0f;\n                inside_word = true;\n                s = next_s;\n                continue;\n            }\n            if (c == '\\r')\n            {\n                s = next_s;\n                continue;\n            }\n        }\n\n        const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX);\n        if (ImCharIsBlankW(c))\n        {\n            if (inside_word)\n            {\n                line_width += blank_width;\n                blank_width = 0.0f;\n                word_end = s;\n            }\n            blank_width += char_width;\n            inside_word = false;\n        }\n        else\n        {\n            word_width += char_width;\n            if (inside_word)\n            {\n                word_end = next_s;\n            }\n            else\n            {\n                prev_word_end = word_end;\n                line_width += word_width + blank_width;\n                word_width = blank_width = 0.0f;\n            }\n\n            // Allow wrapping after punctuation.\n            inside_word = (c != '.' && c != ',' && c != ';' && c != '!' && c != '?' && c != '\\\"');\n        }\n\n        // We ignore blank width at the end of the line (they can be skipped)\n        if (line_width + word_width > wrap_width)\n        {\n            // Words that cannot possibly fit within an entire line will be cut anywhere.\n            if (word_width < wrap_width)\n                s = prev_word_end ? prev_word_end : word_end;\n            break;\n        }\n\n        s = next_s;\n    }\n\n    // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.\n    // +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol).\n    if (s == text && text < text_end)\n        return s + 1;\n    return s;\n}\n\nImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining) const\n{\n    if (!text_end)\n        text_end = text_begin + strlen(text_begin); // FIXME-OPT: Need to avoid this.\n\n    const float line_height = size;\n    const float scale = size / FontSize;\n\n    ImVec2 text_size = ImVec2(0, 0);\n    float line_width = 0.0f;\n\n    const bool word_wrap_enabled = (wrap_width > 0.0f);\n    const char* word_wrap_eol = NULL;\n\n    const char* s = text_begin;\n    while (s < text_end)\n    {\n        if (word_wrap_enabled)\n        {\n            // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.\n            if (!word_wrap_eol)\n                word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width);\n\n            if (s >= word_wrap_eol)\n            {\n                if (text_size.x < line_width)\n                    text_size.x = line_width;\n                text_size.y += line_height;\n                line_width = 0.0f;\n                word_wrap_eol = NULL;\n                s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks\n                continue;\n            }\n        }\n\n        // Decode and advance source\n        const char* prev_s = s;\n        unsigned int c = (unsigned int)*s;\n        if (c < 0x80)\n            s += 1;\n        else\n            s += ImTextCharFromUtf8(&c, s, text_end);\n\n        if (c < 32)\n        {\n            if (c == '\\n')\n            {\n                text_size.x = ImMax(text_size.x, line_width);\n                text_size.y += line_height;\n                line_width = 0.0f;\n                continue;\n            }\n            if (c == '\\r')\n                continue;\n        }\n\n        const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX) * scale;\n        if (line_width + char_width >= max_width)\n        {\n            s = prev_s;\n            break;\n        }\n\n        line_width += char_width;\n    }\n\n    if (text_size.x < line_width)\n        text_size.x = line_width;\n\n    if (line_width > 0 || text_size.y == 0.0f)\n        text_size.y += line_height;\n\n    if (remaining)\n        *remaining = s;\n\n    return text_size;\n}\n\n// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.\nvoid ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, ImWchar c) const\n{\n    const ImFontGlyph* glyph = FindGlyph(c);\n    if (!glyph || !glyph->Visible)\n        return;\n    if (glyph->Colored)\n        col |= ~IM_COL32_A_MASK;\n    float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f;\n    float x = IM_TRUNC(pos.x);\n    float y = IM_TRUNC(pos.y);\n    draw_list->PrimReserve(6, 4);\n    draw_list->PrimRectUV(ImVec2(x + glyph->X0 * scale, y + glyph->Y0 * scale), ImVec2(x + glyph->X1 * scale, y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col);\n}\n\n// Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound.\nvoid ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const\n{\n    if (!text_end)\n        text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls.\n\n    // Align to be pixel perfect\n    float x = IM_TRUNC(pos.x);\n    float y = IM_TRUNC(pos.y);\n    if (y > clip_rect.w)\n        return;\n\n    const float start_x = x;\n    const float scale = size / FontSize;\n    const float line_height = FontSize * scale;\n    const bool word_wrap_enabled = (wrap_width > 0.0f);\n\n    // Fast-forward to first visible line\n    const char* s = text_begin;\n    if (y + line_height < clip_rect.y)\n        while (y + line_height < clip_rect.y && s < text_end)\n        {\n            const char* line_end = (const char*)memchr(s, '\\n', text_end - s);\n            if (word_wrap_enabled)\n            {\n                // FIXME-OPT: This is not optimal as do first do a search for \\n before calling CalcWordWrapPositionA().\n                // If the specs for CalcWordWrapPositionA() were reworked to optionally return on \\n we could combine both.\n                // However it is still better than nothing performing the fast-forward!\n                s = CalcWordWrapPositionA(scale, s, line_end ? line_end : text_end, wrap_width);\n                s = CalcWordWrapNextLineStartA(s, text_end);\n            }\n            else\n            {\n                s = line_end ? line_end + 1 : text_end;\n            }\n            y += line_height;\n        }\n\n    // For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve()\n    // Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer without a newline will likely crash atm)\n    if (text_end - s > 10000 && !word_wrap_enabled)\n    {\n        const char* s_end = s;\n        float y_end = y;\n        while (y_end < clip_rect.w && s_end < text_end)\n        {\n            s_end = (const char*)memchr(s_end, '\\n', text_end - s_end);\n            s_end = s_end ? s_end + 1 : text_end;\n            y_end += line_height;\n        }\n        text_end = s_end;\n    }\n    if (s == text_end)\n        return;\n\n    // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized)\n    const int vtx_count_max = (int)(text_end - s) * 4;\n    const int idx_count_max = (int)(text_end - s) * 6;\n    const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max;\n    draw_list->PrimReserve(idx_count_max, vtx_count_max);\n    ImDrawVert*  vtx_write = draw_list->_VtxWritePtr;\n    ImDrawIdx*   idx_write = draw_list->_IdxWritePtr;\n    unsigned int vtx_index = draw_list->_VtxCurrentIdx;\n\n    const ImU32 col_untinted = col | ~IM_COL32_A_MASK;\n    const char* word_wrap_eol = NULL;\n\n    while (s < text_end)\n    {\n        if (word_wrap_enabled)\n        {\n            // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature.\n            if (!word_wrap_eol)\n                word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - start_x));\n\n            if (s >= word_wrap_eol)\n            {\n                x = start_x;\n                y += line_height;\n                word_wrap_eol = NULL;\n                s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks\n                continue;\n            }\n        }\n\n        // Decode and advance source\n        unsigned int c = (unsigned int)*s;\n        if (c < 0x80)\n            s += 1;\n        else\n            s += ImTextCharFromUtf8(&c, s, text_end);\n\n        if (c < 32)\n        {\n            if (c == '\\n')\n            {\n                x = start_x;\n                y += line_height;\n                if (y > clip_rect.w)\n                    break; // break out of main loop\n                continue;\n            }\n            if (c == '\\r')\n                continue;\n        }\n\n        const ImFontGlyph* glyph = FindGlyph((ImWchar)c);\n        if (glyph == NULL)\n            continue;\n\n        float char_width = glyph->AdvanceX * scale;\n        if (glyph->Visible)\n        {\n            // We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w\n            float x1 = x + glyph->X0 * scale;\n            float x2 = x + glyph->X1 * scale;\n            float y1 = y + glyph->Y0 * scale;\n            float y2 = y + glyph->Y1 * scale;\n            if (x1 <= clip_rect.z && x2 >= clip_rect.x)\n            {\n                // Render a character\n                float u1 = glyph->U0;\n                float v1 = glyph->V0;\n                float u2 = glyph->U1;\n                float v2 = glyph->V1;\n\n                // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads.\n                if (cpu_fine_clip)\n                {\n                    if (x1 < clip_rect.x)\n                    {\n                        u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1);\n                        x1 = clip_rect.x;\n                    }\n                    if (y1 < clip_rect.y)\n                    {\n                        v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1);\n                        y1 = clip_rect.y;\n                    }\n                    if (x2 > clip_rect.z)\n                    {\n                        u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1);\n                        x2 = clip_rect.z;\n                    }\n                    if (y2 > clip_rect.w)\n                    {\n                        v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1);\n                        y2 = clip_rect.w;\n                    }\n                    if (y1 >= y2)\n                    {\n                        x += char_width;\n                        continue;\n                    }\n                }\n\n                // Support for untinted glyphs\n                ImU32 glyph_col = glyph->Colored ? col_untinted : col;\n\n                // We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here:\n                {\n                    vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = glyph_col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1;\n                    vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = glyph_col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1;\n                    vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = glyph_col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2;\n                    vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = glyph_col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2;\n                    idx_write[0] = (ImDrawIdx)(vtx_index); idx_write[1] = (ImDrawIdx)(vtx_index + 1); idx_write[2] = (ImDrawIdx)(vtx_index + 2);\n                    idx_write[3] = (ImDrawIdx)(vtx_index); idx_write[4] = (ImDrawIdx)(vtx_index + 2); idx_write[5] = (ImDrawIdx)(vtx_index + 3);\n                    vtx_write += 4;\n                    vtx_index += 4;\n                    idx_write += 6;\n                }\n            }\n        }\n        x += char_width;\n    }\n\n    // Give back unused vertices (clipped ones, blanks) ~ this is essentially a PrimUnreserve() action.\n    draw_list->VtxBuffer.Size = (int)(vtx_write - draw_list->VtxBuffer.Data); // Same as calling shrink()\n    draw_list->IdxBuffer.Size = (int)(idx_write - draw_list->IdxBuffer.Data);\n    draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size);\n    draw_list->_VtxWritePtr = vtx_write;\n    draw_list->_IdxWritePtr = idx_write;\n    draw_list->_VtxCurrentIdx = vtx_index;\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGui Internal Render Helpers\n//-----------------------------------------------------------------------------\n// Vaguely redesigned to stop accessing ImGui global state:\n// - RenderArrow()\n// - RenderBullet()\n// - RenderCheckMark()\n// - RenderArrowPointingAt()\n// - RenderRectFilledRangeH()\n// - RenderRectFilledWithHole()\n//-----------------------------------------------------------------------------\n// Function in need of a redesign (legacy mess)\n// - RenderColorRectWithAlphaCheckerboard()\n//-----------------------------------------------------------------------------\n\n// Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state\nvoid ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale)\n{\n    const float h = draw_list->_Data->FontSize * 1.00f;\n    float r = h * 0.40f * scale;\n    ImVec2 center = pos + ImVec2(h * 0.50f, h * 0.50f * scale);\n\n    ImVec2 a, b, c;\n    switch (dir)\n    {\n    case ImGuiDir_Up:\n    case ImGuiDir_Down:\n        if (dir == ImGuiDir_Up) r = -r;\n        a = ImVec2(+0.000f, +0.750f) * r;\n        b = ImVec2(-0.866f, -0.750f) * r;\n        c = ImVec2(+0.866f, -0.750f) * r;\n        break;\n    case ImGuiDir_Left:\n    case ImGuiDir_Right:\n        if (dir == ImGuiDir_Left) r = -r;\n        a = ImVec2(+0.750f, +0.000f) * r;\n        b = ImVec2(-0.750f, +0.866f) * r;\n        c = ImVec2(-0.750f, -0.866f) * r;\n        break;\n    case ImGuiDir_None:\n    case ImGuiDir_COUNT:\n        IM_ASSERT(0);\n        break;\n    }\n    draw_list->AddTriangleFilled(center + a, center + b, center + c, col);\n}\n\nvoid ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col)\n{\n    // FIXME-OPT: This should be baked in font.\n    draw_list->AddCircleFilled(pos, draw_list->_Data->FontSize * 0.20f, col, 8);\n}\n\nvoid ImGui::RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz)\n{\n    float thickness = ImMax(sz / 5.0f, 1.0f);\n    sz -= thickness * 0.5f;\n    pos += ImVec2(thickness * 0.25f, thickness * 0.25f);\n\n    float third = sz / 3.0f;\n    float bx = pos.x + third;\n    float by = pos.y + sz - third * 0.5f;\n    draw_list->PathLineTo(ImVec2(bx - third, by - third));\n    draw_list->PathLineTo(ImVec2(bx, by));\n    draw_list->PathLineTo(ImVec2(bx + third * 2.0f, by - third * 2.0f));\n    draw_list->PathStroke(col, 0, thickness);\n}\n\n// Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side.\nvoid ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col)\n{\n    switch (direction)\n    {\n    case ImGuiDir_Left:  draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return;\n    case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return;\n    case ImGuiDir_Up:    draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return;\n    case ImGuiDir_Down:  draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return;\n    case ImGuiDir_None: case ImGuiDir_COUNT: break; // Fix warnings\n    }\n}\n\nstatic inline float ImAcos01(float x)\n{\n    if (x <= 0.0f) return IM_PI * 0.5f;\n    if (x >= 1.0f) return 0.0f;\n    return ImAcos(x);\n    //return (-0.69813170079773212f * x * x - 0.87266462599716477f) * x + 1.5707963267948966f; // Cheap approximation, may be enough for what we do.\n}\n\n// FIXME: Cleanup and move code to ImDrawList.\nvoid ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding)\n{\n    if (x_end_norm == x_start_norm)\n        return;\n    if (x_start_norm > x_end_norm)\n        ImSwap(x_start_norm, x_end_norm);\n\n    ImVec2 p0 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_start_norm), rect.Min.y);\n    ImVec2 p1 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_end_norm), rect.Max.y);\n    if (rounding == 0.0f)\n    {\n        draw_list->AddRectFilled(p0, p1, col, 0.0f);\n        return;\n    }\n\n    rounding = ImClamp(ImMin((rect.Max.x - rect.Min.x) * 0.5f, (rect.Max.y - rect.Min.y) * 0.5f) - 1.0f, 0.0f, rounding);\n    const float inv_rounding = 1.0f / rounding;\n    const float arc0_b = ImAcos01(1.0f - (p0.x - rect.Min.x) * inv_rounding);\n    const float arc0_e = ImAcos01(1.0f - (p1.x - rect.Min.x) * inv_rounding);\n    const float half_pi = IM_PI * 0.5f; // We will == compare to this because we know this is the exact value ImAcos01 can return.\n    const float x0 = ImMax(p0.x, rect.Min.x + rounding);\n    if (arc0_b == arc0_e)\n    {\n        draw_list->PathLineTo(ImVec2(x0, p1.y));\n        draw_list->PathLineTo(ImVec2(x0, p0.y));\n    }\n    else if (arc0_b == 0.0f && arc0_e == half_pi)\n    {\n        draw_list->PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3, 6); // BL\n        draw_list->PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6, 9); // TR\n    }\n    else\n    {\n        draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b, 3); // BL\n        draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e, 3); // TR\n    }\n    if (p1.x > rect.Min.x + rounding)\n    {\n        const float arc1_b = ImAcos01(1.0f - (rect.Max.x - p1.x) * inv_rounding);\n        const float arc1_e = ImAcos01(1.0f - (rect.Max.x - p0.x) * inv_rounding);\n        const float x1 = ImMin(p1.x, rect.Max.x - rounding);\n        if (arc1_b == arc1_e)\n        {\n            draw_list->PathLineTo(ImVec2(x1, p0.y));\n            draw_list->PathLineTo(ImVec2(x1, p1.y));\n        }\n        else if (arc1_b == 0.0f && arc1_e == half_pi)\n        {\n            draw_list->PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9, 12); // TR\n            draw_list->PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0, 3);  // BR\n        }\n        else\n        {\n            draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b, 3); // TR\n            draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e, 3); // BR\n        }\n    }\n    draw_list->PathFillConvex(col);\n}\n\nvoid ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding)\n{\n    const bool fill_L = (inner.Min.x > outer.Min.x);\n    const bool fill_R = (inner.Max.x < outer.Max.x);\n    const bool fill_U = (inner.Min.y > outer.Min.y);\n    const bool fill_D = (inner.Max.y < outer.Max.y);\n    if (fill_L) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Min.y), ImVec2(inner.Min.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopLeft)    | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomLeft));\n    if (fill_R) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Min.y), ImVec2(outer.Max.x, inner.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_U ? 0 : ImDrawFlags_RoundCornersTopRight)   | (fill_D ? 0 : ImDrawFlags_RoundCornersBottomRight));\n    if (fill_U) draw_list->AddRectFilled(ImVec2(inner.Min.x, outer.Min.y), ImVec2(inner.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersTopLeft)    | (fill_R ? 0 : ImDrawFlags_RoundCornersTopRight));\n    if (fill_D) draw_list->AddRectFilled(ImVec2(inner.Min.x, inner.Max.y), ImVec2(inner.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersNone | (fill_L ? 0 : ImDrawFlags_RoundCornersBottomLeft) | (fill_R ? 0 : ImDrawFlags_RoundCornersBottomRight));\n    if (fill_L && fill_U) draw_list->AddRectFilled(ImVec2(outer.Min.x, outer.Min.y), ImVec2(inner.Min.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopLeft);\n    if (fill_R && fill_U) draw_list->AddRectFilled(ImVec2(inner.Max.x, outer.Min.y), ImVec2(outer.Max.x, inner.Min.y), col, rounding, ImDrawFlags_RoundCornersTopRight);\n    if (fill_L && fill_D) draw_list->AddRectFilled(ImVec2(outer.Min.x, inner.Max.y), ImVec2(inner.Min.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomLeft);\n    if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight);\n}\n\n// Helper for ColorPicker4()\n// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that.\n// Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether.\n// FIXME: uses ImGui::GetColorU32\nvoid ImGui::RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, ImDrawFlags flags)\n{\n    if ((flags & ImDrawFlags_RoundCornersMask_) == 0)\n        flags = ImDrawFlags_RoundCornersDefault_;\n    if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF)\n    {\n        ImU32 col_bg1 = GetColorU32(ImAlphaBlendColors(IM_COL32(204, 204, 204, 255), col));\n        ImU32 col_bg2 = GetColorU32(ImAlphaBlendColors(IM_COL32(128, 128, 128, 255), col));\n        draw_list->AddRectFilled(p_min, p_max, col_bg1, rounding, flags);\n\n        int yi = 0;\n        for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++)\n        {\n            float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y);\n            if (y2 <= y1)\n                continue;\n            for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f)\n            {\n                float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x);\n                if (x2 <= x1)\n                    continue;\n                ImDrawFlags cell_flags = ImDrawFlags_RoundCornersNone;\n                if (y1 <= p_min.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersTopLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersTopRight; }\n                if (y2 >= p_max.y) { if (x1 <= p_min.x) cell_flags |= ImDrawFlags_RoundCornersBottomLeft; if (x2 >= p_max.x) cell_flags |= ImDrawFlags_RoundCornersBottomRight; }\n\n                // Combine flags\n                cell_flags = (flags == ImDrawFlags_RoundCornersNone || cell_flags == ImDrawFlags_RoundCornersNone) ? ImDrawFlags_RoundCornersNone : (cell_flags & flags);\n                draw_list->AddRectFilled(ImVec2(x1, y1), ImVec2(x2, y2), col_bg2, rounding, cell_flags);\n            }\n        }\n    }\n    else\n    {\n        draw_list->AddRectFilled(p_min, p_max, col, rounding, flags);\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Decompression code\n//-----------------------------------------------------------------------------\n// Compressed with stb_compress() then converted to a C array and encoded as base85.\n// Use the program in misc/fonts/binary_to_compressed_c.cpp to create the array from a TTF file.\n// The purpose of encoding as base85 instead of \"0x00,0x01,...\" style is only save on _source code_ size.\n// Decompression from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h\n//-----------------------------------------------------------------------------\n\nstatic unsigned int stb_decompress_length(const unsigned char *input)\n{\n    return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11];\n}\n\nstatic unsigned char *stb__barrier_out_e, *stb__barrier_out_b;\nstatic const unsigned char *stb__barrier_in_b;\nstatic unsigned char *stb__dout;\nstatic void stb__match(const unsigned char *data, unsigned int length)\n{\n    // INVERSE of memmove... write each byte before copying the next...\n    IM_ASSERT(stb__dout + length <= stb__barrier_out_e);\n    if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; }\n    if (data < stb__barrier_out_b) { stb__dout = stb__barrier_out_e+1; return; }\n    while (length--) *stb__dout++ = *data++;\n}\n\nstatic void stb__lit(const unsigned char *data, unsigned int length)\n{\n    IM_ASSERT(stb__dout + length <= stb__barrier_out_e);\n    if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; }\n    if (data < stb__barrier_in_b) { stb__dout = stb__barrier_out_e+1; return; }\n    memcpy(stb__dout, data, length);\n    stb__dout += length;\n}\n\n#define stb__in2(x)   ((i[x] << 8) + i[(x)+1])\n#define stb__in3(x)   ((i[x] << 16) + stb__in2((x)+1))\n#define stb__in4(x)   ((i[x] << 24) + stb__in3((x)+1))\n\nstatic const unsigned char *stb_decompress_token(const unsigned char *i)\n{\n    if (*i >= 0x20) { // use fewer if's for cases that expand small\n        if (*i >= 0x80)       stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2;\n        else if (*i >= 0x40)  stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3;\n        else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1);\n    } else { // more ifs for cases that expand large, since overhead is amortized\n        if (*i >= 0x18)       stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4;\n        else if (*i >= 0x10)  stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5;\n        else if (*i >= 0x08)  stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1);\n        else if (*i == 0x07)  stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1);\n        else if (*i == 0x06)  stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5;\n        else if (*i == 0x04)  stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6;\n    }\n    return i;\n}\n\nstatic unsigned int stb_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen)\n{\n    const unsigned long ADLER_MOD = 65521;\n    unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16;\n    unsigned long blocklen = buflen % 5552;\n\n    unsigned long i;\n    while (buflen) {\n        for (i=0; i + 7 < blocklen; i += 8) {\n            s1 += buffer[0], s2 += s1;\n            s1 += buffer[1], s2 += s1;\n            s1 += buffer[2], s2 += s1;\n            s1 += buffer[3], s2 += s1;\n            s1 += buffer[4], s2 += s1;\n            s1 += buffer[5], s2 += s1;\n            s1 += buffer[6], s2 += s1;\n            s1 += buffer[7], s2 += s1;\n\n            buffer += 8;\n        }\n\n        for (; i < blocklen; ++i)\n            s1 += *buffer++, s2 += s1;\n\n        s1 %= ADLER_MOD, s2 %= ADLER_MOD;\n        buflen -= blocklen;\n        blocklen = 5552;\n    }\n    return (unsigned int)(s2 << 16) + (unsigned int)s1;\n}\n\nstatic unsigned int stb_decompress(unsigned char *output, const unsigned char *i, unsigned int /*length*/)\n{\n    if (stb__in4(0) != 0x57bC0000) return 0;\n    if (stb__in4(4) != 0)          return 0; // error! stream is > 4GB\n    const unsigned int olen = stb_decompress_length(i);\n    stb__barrier_in_b = i;\n    stb__barrier_out_e = output + olen;\n    stb__barrier_out_b = output;\n    i += 16;\n\n    stb__dout = output;\n    for (;;) {\n        const unsigned char *old_i = i;\n        i = stb_decompress_token(i);\n        if (i == old_i) {\n            if (*i == 0x05 && i[1] == 0xfa) {\n                IM_ASSERT(stb__dout == output + olen);\n                if (stb__dout != output + olen) return 0;\n                if (stb_adler32(1, output, olen) != (unsigned int) stb__in4(2))\n                    return 0;\n                return olen;\n            } else {\n                IM_ASSERT(0); /* NOTREACHED */\n                return 0;\n            }\n        }\n        IM_ASSERT(stb__dout <= output + olen);\n        if (stb__dout > output + olen)\n            return 0;\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Default font data (ProggyClean.ttf)\n//-----------------------------------------------------------------------------\n// ProggyClean.ttf\n// Copyright (c) 2004, 2005 Tristan Grimmer\n// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip)\n// Download and more information at http://upperbounds.net\n//-----------------------------------------------------------------------------\n// File: 'ProggyClean.ttf' (41208 bytes)\n// Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding).\n// The purpose of encoding as base85 instead of \"0x00,0x01,...\" style is only save on _source code_ size.\n//-----------------------------------------------------------------------------\nstatic const char proggy_clean_ttf_compressed_data_base85[11980 + 1] =\n    \"7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/\"\n    \"2*>]b(MC;$jPfY.;h^`IWM9<Lh2TlS+f-s$o6Q<BWH`YiU.xfLq$N;$0iR/GX:U(jcW2p/W*q?-qmnUCI;jHSAiFWM.R*kU@C=GH?a9wp8f$e.-4^Qg1)Q-GL(lf(r/7GrRgwV%MS=C#\"\n    \"`8ND>Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1<q-UE31#^-V'8IRUo7Qf./L>=Ke$$'5F%)]0^#0X@U.a<r:QLtFsLcL6##lOj)#.Y5<-R&KgLwqJfLgN&;Q?gI^#DY2uL\"\n    \"i@^rMl9t=cWq6##weg>$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;-<nLENhvx>-VsM.M0rJfLH2eTM`*oJMHRC`N\"\n    \"kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`&#0j@'DbG&#^$PG.Ll+DNa<XCMKEV*N)LN/N\"\n    \"*b=%Q6pia-Xg8I$<MR&,VdJe$<(7G;Ckl'&hF;;$<_=X(b.RS%%)###MPBuuE1V:v&cX&#2m#(&cV]`k9OhLMbn%s$G2,B$BfD3X*sp5#l,$R#]x_X1xKX%b5U*[r5iMfUo9U`N99hG)\"\n    \"tm+/Us9pG)XPu`<0s-)WTt(gCRxIg(%6sfh=ktMKn3j)<6<b5Sk_/0(^]AaN#(p/L>&VZ>1i%h1S9u5o@YaaW$e+b<TWFn/Z:Oh(Cx2$lNEoN^e)#CFY@@I;BOQ*sRwZtZxRcU7uW6CX\"\n    \"ow0i(?$Q[cjOd[P4d)]>ROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc.\"\n    \"x]Ip.PH^'/aqUO/$1WxLoW0[iLA<QT;5HKD+@qQ'NQ(3_PLhE48R.qAPSwQ0/WK?Z,[x?-J;jQTWA0X@KJ(_Y8N-:/M74:/-ZpKrUss?d#dZq]DAbkU*JqkL+nwX@@47`5>w=4h(9.`G\"\n    \"CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?G<Nald$qs]@]L<J7bR*>gv:[7MI2k).'2($5FNP&EQ(,)\"\n    \"U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#\"\n    \"'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM\"\n    \"_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0<q-]L_?^)1vw'.,MRsqVr.L;aN&#/EgJ)PBc[-f>+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu\"\n    \"Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/\"\n    \"/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[K<L\"\n    \"%a2E-grWVM3@2=-k22tL]4$##6We'8UJCKE[d_=%wI;'6X-GsLX4j^SgJ$##R*w,vP3wK#iiW&#*h^D&R?jp7+/u&#(AP##XU8c$fSYW-J95_-Dp[g9wcO&#M-h1OcJlc-*vpw0xUX&#\"\n    \"OQFKNX@QI'IoPp7nb,QU//MQ&ZDkKP)X<WSVL(68uVl&#c'[0#(s1X&xm$Y%B7*K:eDA323j998GXbA#pwMs-jgD$9QISB-A_(aN4xoFM^@C58D0+Q+q3n0#3U1InDjF682-SjMXJK)(\"\n    \"h$hxua_K]ul92%'BOU&#BRRh-slg8KDlr:%L71Ka:.A;%YULjDPmL<LYs8i#XwJOYaKPKc1h:'9Ke,g)b),78=I39B;xiY$bgGw-&.Zi9InXDuYa%G*f2Bq7mn9^#p1vv%#(Wi-;/Z5h\"\n    \"o;#2:;%d&#x9v68C5g?ntX0X)pT`;%pB3q7mgGN)3%(P8nTd5L7GeA-GL@+%J3u2:(Yf>et`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO\"\n    \"j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J<j$UpK<Q4a1]MupW^-\"\n    \"sj_$%[HK%'F####QRZJ::Y3EGl4'@%FkiAOg#p[##O`gukTfBHagL<LHw%q&OV0##F=6/:chIm0@eCP8X]:kFI%hl8hgO@RcBhS-@Qb$%+m=hPDLg*%K8ln(wcf3/'DW-$.lR?n[nCH-\"\n    \"eXOONTJlh:.RYF%3'p6sq:UIMA945&^HFS87@$EP2iG<-lCO$%c`uKGD3rC$x0BL8aFn--`ke%#HMP'vh1/R&O_J9'um,.<tx[@%wsJk&bUT2`0uMv7gg#qp/ij.L56'hl;.s5CUrxjO\"\n    \"M7-##.l+Au'A&O:-T72L]P`&=;ctp'XScX*rU.>-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%\"\n    \"LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$M<Jnq79VsJW/mWS*PUiq76;]/NM_>hLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]\"\n    \"%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et\"\n    \"Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$<M-SGZ':+Q_k+uvOSLiEo(<aD/K<CCc`'Lx>'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:\"\n    \"a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VB<HFF*qL(\"\n    \"$/V,;(kXZejWO`<[5?\\?ewY(*9=%wDc;,u<'9t3W-(H1th3+G]ucQ]kLs7df($/*JL]@*t7Bu_G3_7mp7<iaQjO@.kLg;x3B0lqp7Hf,^Ze7-##@/c58Mo(3;knp0%)A7?-W+eI'o8)b<\"\n    \"nKnw'Ho8C=Y>pqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<<aG/1N$#FX$0V5Y6x'aErI3I$7x%E`v<-BY,)%-?Psf*l?%C3.mM(=/M0:JxG'?\"\n    \"7WhH%o'a<-80g0NBxoO(GH<dM]n.+%q@jH?f.UsJ2Ggs&4<-e47&Kl+f//9@`b+?.TeN_&B8Ss?v;^Trk;f#YvJkl&w$]>-+k?'(<S:68tq*WoDfZu';mM?8X[ma8W%*`-=;D.(nc7/;\"\n    \")g:T1=^J$&BRV(-lTmNB6xqB[@0*o.erM*<SWF]u2=st-*(6v>^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M\"\n    \"D?@f&1'BW-)Ju<L25gl8uhVm1hL$##*8###'A3/LkKW+(^rWX?5W_8g)a(m&K8P>#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(\"\n    \"P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs\"\n    \"bIu)'Z,*[>br5fX^:FPAWr-m2KgL<LUN098kTF&#lvo58=/vjDo;.;)Ka*hLR#/k=rKbxuV`>Q_nN6'8uTG&#1T5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q\"\n    \"h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aeg<Z'<$#4H)6,>e0jT6'N#(q%.O=?2S]u*(m<-\"\n    \"V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i\"\n    \"sZ88+dKQ)W6>J%CL<KE>`.d*(B`-n8D9oK<Up]c$X$(,)M8Zt7/[rdkqTgl-0cuGMv'?>-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P&#9r+$%CE=68>K8r0=dSC%%(@p7\"\n    \".m7jilQ02'0-VWAg<a/''3u.=4L$Y)6k/K:_[3=&jvL<L0C/2'v:^;-DIBW,B4E68:kZ;%?8(Q8BH=kO65BW?xSG&#@uU,DS*,?.+(o(#1vCS8#CHF>TlGW'b)Tq7VT9q^*^$$.:&N@@\"\n    \"$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*\"\n    \"hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u\"\n    \"@-W$U%VEQ/,,>>#)D<h#`)h0:<Q6909ua+&VU%n2:cG3FJ-%@Bj-DgLr`Hw&HAKjKjseK</xKT*)B,N9X3]krc12t'pgTV(Lv-tL[xg_%=M_q7a^x?7Ubd>#%8cY#YZ?=,`Wdxu/ae&#\"\n    \"w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$s<Eh#c&)q.MXI%#v9ROa5FZO%sF7q7Nwb&#ptUJ:aqJe$Sl68%.D###EC><?-aF&#RNQv>o8lKN%5/$(vdfq7+ebA#\"\n    \"u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(<c`Q8N)jEIF*+?P2a8g%)$q]o2aH8C&<SibC/q,(e:v;-b#6[$NtDZ84Je2KNvB#$P5?tQ3nt(0\"\n    \"d=j.LQf./Ll33+(;q3L-w=8dX$#WF&uIJ@-bfI>%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoF&#4DoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8\"\n    \"6e%B/:=>)N4xeW.*wft-;$'58-ESqr<b?UI(_%@[P46>#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#\"\n    \"b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjL<Lni;''X.`$#8+1GD\"\n    \":k$YUWsbn8ogh6rxZ2Z9]%nd+>V#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#<NEdtg(n'=S1A(Q1/I&4([%dM`,Iu'1:_hL>SfD07&6D<fp8dHM7/g+\"\n    \"tlPN9J*rKaPct&?'uBCem^jn%9_K)<,C5K3s=5g&GmJb*[SYq7K;TRLGCsM-$$;S%:Y@r7AK0pprpL<Lrh,q7e/%KWK:50I^+m'vi`3?%Zp+<-d+$L-Sv:@.o19n$s0&39;kn;S%BSq*\"\n    \"$3WoJSCLweV[aZ'MQIjO<7;X-X;&+dMLvu#^UsGEC9WEc[X(wI7#2.(F0jV*eZf<-Qv3J-c+J5AlrB#$p(H68LvEA'q3n0#m,[`*8Ft)FcYgEud]CWfm68,(aLA$@EFTgLXoBq/UPlp7\"\n    \":d[/;r_ix=:TF`S5H-b<LI&HY(K=h#)]Lk$K14lVfm:x$H<3^Ql<M`$OhapBnkup'D#L$Pb_`N*g]2e;X/Dtg,bsj&K#2[-:iYr'_wgH)NUIR8a1n#S?Yej'h8^58UbZd+^FKD*T@;6A\"\n    \"7aQC[K8d-(v6GI$x:T<&'Gp5Uf>@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-<aN((^7('#Z0wK#5GX@7\"\n    \"u][`*S^43933A4rl][`*O4CgLEl]v$1Q3AeF37dbXk,.)vj#x'd`;qgbQR%FW,2(?LO=s%Sc68%NP'##Aotl8x=BE#j1UD([3$M(]UI2LX3RpKN@;/#f'f/&_mt&F)XdF<9t4)Qa.*kT\"\n    \"LwQ'(TTB9.xH'>#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5<N?)NBS)QN*_I,?&)2'IM%L3I)X((e/dl2&8'<M\"\n    \":^#M*Q+[T.Xri.LYS3v%fF`68h;b-X[/En'CR.q7E)p'/kle2HM,u;^%OKC-N+Ll%F9CF<Nf'^#t2L,;27W:0O@6##U6W7:$rJfLWHj$#)woqBefIZ.PK<b*t7ed;p*_m;4ExK#h@&]>\"\n    \"_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%\"\n    \"hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;\"\n    \"^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmL<LD)F^%[tC'8;+9E#C$g%#5Y>q9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:\"\n    \"+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3<n-&%H%b<FDj2M<hH=&Eh<2Len$b*aTX=-8QxN)k11IM1c^j%\"\n    \"9s<L<NFSo)B?+<-(GxsF,^-Eh@$4dXhN$+#rxK8'je'D7k`e;)2pYwPA'_p9&@^18ml1^[@g4t*[JOa*[=Qp7(qJ_oOL^('7fB&Hq-:sf,sNj8xq^>$U4O]GKx'm9)b@p7YsvK3w^YR-\"\n    \"CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*\"\n    \"hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdF<TddF<9Ah-6&9tWoDlh]&1SpGMq>Ti1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IX<N+T+0MlMBPQ*Vj>SsD<U4JHY\"\n    \"8kD2)2fU/M#$e.)T4,_=8hLim[&);?UkK'-x?'(:siIfL<$pFM`i<?%W(mGDHM%>iWP,##P`%/L<eXi:@Z9C.7o=@(pXdAO/NLQ8lPl+HPOQa8wD8=^GlPa8TKI1CjhsCTSLJM'/Wl>-\"\n    \"S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n<bhPmUkMw>%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL<LoNs'6,'85`\"\n    \"0?t/'_U59@]ddF<#LdF<eWdF<OuN/45rY<-L@&#+fm>69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdF<gR@2L=FNU-<b[(9c/ML3m;Z[$oF3g)GAWqpARc=<ROu7cL5l;-[A]%/\"\n    \"+fsd;l#SafT/f*W]0=O'$(Tb<[)*@e775R-:Yob%g*>l*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj\"\n    \"M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#<IGe;__.thjZl<%w(Wk2xmp4Q@I#I9,DF]u7-P=.-_:YJ]aS@V\"\n    \"?6*C()dOp7:WL,b&3Rg/.cmM9&r^>$(>.Z-I&J(Q0Hd5Q%7Co-b`-c<N(6r@ip+AurK<m86QIth*#v;-OBqi+L7wDE-Ir8K['m+DDSLwK&/.?-V%U_%3:qKNu$_b*B-kp7NaD'QdWQPK\"\n    \"Yq[@>P)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8<FfNkgg^oIbah*#8/Qt$F&:K*-(N/'+1vMB,u()-a.VUU*#[e%gAAO(S>WlA2);Sa\"\n    \">gXm8YB`1d@K#n]76-a$U,mF<fX]idqd)<3,]J7JmW4`6]uks=4-72L(jEk+:bJ0M^q-8Dm_Z?0olP1C9Sa&H[d&c$ooQUj]Exd*3ZM@-WGW2%s',B-_M%>%Ul:#/'xoFM9QX-$.QN'>\"\n    \"[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B</R90;eZ]%Ncq;-Tl]#F>2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I\"\n    \"wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1<Vc52=u`3^o-n1'g4v58Hj&6_t7$##?M)c<$bgQ_'SY((-xkA#\"\n    \"Y(,p'H9rIVY-b,'%bCPF7.J<Up^,(dU1VY*5#WkTU>h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-u<Hp,3@e^9UB1J+ak9-TN/mhKPg+AJYd$\"\n    \"MlvAF_jCK*.O-^(63adMT->W%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)\"\n    \"i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo\"\n    \"1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P\"\n    \"iDDG)g,r%+?,$@?uou5tSe2aN_AQU*<h`e-GI7)?OK2A.d7_c)?wQ5AS@DL3r#7fSkgl6-++D:'A,uq7SvlB$pcpH'q3n0#_%dY#xCpr-l<F0NR@-##FEV6NTF6##$l84N1w?AO>'IAO\"\n    \"URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#\"\n    \";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T<XoIB&hx=T1PcDaB&;HH+-AFr?(m9HZV)FKS8JCw;SD=6[^/DZUL`EUDf]GGlG&>\"\n    \"w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#<xU?#@.i?#D:%@#HF7@#LRI@#P_[@#Tkn@#Xw*A#]-=A#a9OA#\"\n    \"d<F&#*;G##.GY##2Sl##6`($#:l:$#>xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4&#3^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4\"\n    \"A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#\"\n    \"/QHC#3^ZC#7jmC#;v)D#?,<D#C8ND#GDaD#KPsD#O]/E#g1A5#KA*1#gC17#MGd;#8(02#L-d3#rWM4#Hga1#,<w0#T.j<#O#'2#CYN1#qa^:#_4m3#o@/=#eG8=#t8J5#`+78#4uI-#\"\n    \"m3B2#SB[8#Q0@8#i[*9#iOn8#1Nm;#^sN9#qh<9#:=x-#P;K2#$%X9#bC+.#Rg;<#mN=.#MTF.#RZO.#2?)4#Y#(/#[)1/#b;L/#dAU/#0Sv;#lY$0#n`-0#sf60#(F24#wrH0#%/e0#\"\n    \"TmD<#%JSMFove:CTBEXI:<eh2g)B,3h2^G3i;#d3jD>)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP\"\n    \"GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp\"\n    \"O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#\";\n\nstatic const char* GetDefaultCompressedFontDataTTFBase85()\n{\n    return proggy_clean_ttf_compressed_data_base85;\n}\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "third-party/imgui/imgui_impl_sdl2.cpp",
    "content": "// dear imgui: Platform Backend for SDL2\n// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)\n// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)\n// (Prefer SDL 2.0.5+ for full feature support.)\n\n// Implemented features:\n//  [X] Platform: Clipboard support.\n//  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.\n//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]\n//  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.\n//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.\n//  [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, \"1\");' before SDL_CreateWindow()!.\n\n// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.\n// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.\n// Learn about Dear ImGui:\n// - FAQ                  https://dearimgui.com/faq\n// - Getting Started      https://dearimgui.com/getting-started\n// - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).\n// - Introduction, links and more at the top of imgui.cpp\n\n// CHANGELOG\n// (minor and older changes stripped away, please see git history for details)\n//  2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.\n//  2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306)\n//  2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702)\n//  2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644)\n//  2023-02-07: Implement IME handler (io.SetPlatformImeDataFn will call SDL_SetTextInputRect()/SDL_StartTextInput()).\n//  2023-02-07: *BREAKING CHANGE* Renamed this backend file from imgui_impl_sdl.cpp/.h to imgui_impl_sdl2.cpp/.h in prevision for the future release of SDL3.\n//  2023-02-02: Avoid calling SDL_SetCursor() when cursor has not changed, as the function is surprisingly costly on Mac with latest SDL (may be fixed in next SDL version).\n//  2023-02-02: Added support for SDL 2.0.18+ preciseX/preciseY mouse wheel data for smooth scrolling + Scaling X value on Emscripten (bug?). (#4019, #6096)\n//  2023-02-02: Removed SDL_MOUSEWHEEL value clamping, as values seem correct in latest Emscripten. (#4019)\n//  2023-02-01: Flipping SDL_MOUSEWHEEL 'wheel.x' value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)\n//  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.\n//  2022-09-26: Inputs: Disable SDL 2.0.22 new \"auto capture\" (SDL_HINT_MOUSE_AUTO_CAPTURE) which prevents drag and drop across windows for multi-viewport support + don't capture when drag and dropping. (#5710)\n//  2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).\n//  2022-03-22: Inputs: Fix mouse position issues when dragging outside of boundaries. SDL_CaptureMouse() erroneously still gives out LEAVE events when hovering OS decorations.\n//  2022-03-22: Inputs: Added support for extra mouse buttons (SDL_BUTTON_X1/SDL_BUTTON_X2).\n//  2022-02-04: Added SDL_Renderer* parameter to ImGui_ImplSDL2_InitForSDLRenderer(), so we can use SDL_GetRendererOutputSize() instead of SDL_GL_GetDrawableSize() when bound to a SDL_Renderer.\n//  2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.\n//  2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].\n//  2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).\n//  2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates.\n//  2022-01-12: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API.\n//  2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted.\n//  2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.\n//  2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST.\n//  2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+)\n//  2021-06-29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary.\n//  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).\n//  2021-03-22: Rework global mouse pos availability check listing supported platforms explicitly, effectively fixing mouse access on Raspberry Pi. (#2837, #3950)\n//  2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends.\n//  2020-02-20: Inputs: Fixed mapping for ImGuiKey_KeyPadEnter (using SDL_SCANCODE_KP_ENTER instead of SDL_SCANCODE_RETURN2).\n//  2019-12-17: Inputs: On Wayland, use SDL_GetMouseState (because there is no global mouse state).\n//  2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.\n//  2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.\n//  2019-04-23: Inputs: Added support for SDL_GameController (if ImGuiConfigFlags_NavEnableGamepad is set by user application).\n//  2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.\n//  2018-12-21: Inputs: Workaround for Android/iOS which don't seem to handle focus related calls.\n//  2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.\n//  2018-11-14: Changed the signature of ImGui_ImplSDL2_ProcessEvent() to take a 'const SDL_Event*'.\n//  2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.\n//  2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.\n//  2018-06-08: Misc: Extracted imgui_impl_sdl.cpp/.h away from the old combined SDL2+OpenGL/Vulkan examples.\n//  2018-06-08: Misc: ImGui_ImplSDL2_InitForOpenGL() now takes a SDL_GLContext parameter.\n//  2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText).\n//  2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.\n//  2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value.\n//  2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.\n//  2018-02-06: Inputs: Added mapping for ImGuiKey_Space.\n//  2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS).\n//  2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes.\n//  2018-01-20: Inputs: Added Horizontal Mouse Wheel support.\n//  2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS.\n//  2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.\n//  2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).\n//  2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_impl_sdl2.h\"\n\n// Clang warnings with -Weverything\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#endif\n\n// SDL\n#include <SDL.h>\n#include <SDL_syswm.h>\n#if defined(__APPLE__)\n#include <TargetConditionals.h>\n#endif\n\n#if SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__)\n#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE    1\n#else\n#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE    0\n#endif\n#define SDL_HAS_VULKAN                      SDL_VERSION_ATLEAST(2,0,6)\n\n// SDL Data\nstruct ImGui_ImplSDL2_Data\n{\n    SDL_Window*     Window;\n    SDL_Renderer*   Renderer;\n    Uint64          Time;\n    Uint32          MouseWindowID;\n    int             MouseButtonsDown;\n    SDL_Cursor*     MouseCursors[ImGuiMouseCursor_COUNT];\n    SDL_Cursor*     LastMouseCursor;\n    int             PendingMouseLeaveFrame;\n    char*           ClipboardTextData;\n    bool            MouseCanUseGlobalState;\n\n    ImGui_ImplSDL2_Data()   { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts\n// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.\n// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.\n// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.\nstatic ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData()\n{\n    return ImGui::GetCurrentContext() ? (ImGui_ImplSDL2_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;\n}\n\n// Functions\nstatic const char* ImGui_ImplSDL2_GetClipboardText(void*)\n{\n    ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();\n    if (bd->ClipboardTextData)\n        SDL_free(bd->ClipboardTextData);\n    bd->ClipboardTextData = SDL_GetClipboardText();\n    return bd->ClipboardTextData;\n}\n\nstatic void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)\n{\n    SDL_SetClipboardText(text);\n}\n\n// Note: native IME will only display if user calls SDL_SetHint(SDL_HINT_IME_SHOW_UI, \"1\") _before_ SDL_CreateWindow().\nstatic void ImGui_ImplSDL2_SetPlatformImeData(ImGuiViewport*, ImGuiPlatformImeData* data)\n{\n    if (data->WantVisible)\n    {\n        SDL_Rect r;\n        r.x = (int)data->InputPos.x;\n        r.y = (int)data->InputPos.y;\n        r.w = 1;\n        r.h = (int)data->InputLineHeight;\n        SDL_SetTextInputRect(&r);\n    }\n}\n\nstatic ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode)\n{\n    switch (keycode)\n    {\n        case SDLK_TAB: return ImGuiKey_Tab;\n        case SDLK_LEFT: return ImGuiKey_LeftArrow;\n        case SDLK_RIGHT: return ImGuiKey_RightArrow;\n        case SDLK_UP: return ImGuiKey_UpArrow;\n        case SDLK_DOWN: return ImGuiKey_DownArrow;\n        case SDLK_PAGEUP: return ImGuiKey_PageUp;\n        case SDLK_PAGEDOWN: return ImGuiKey_PageDown;\n        case SDLK_HOME: return ImGuiKey_Home;\n        case SDLK_END: return ImGuiKey_End;\n        case SDLK_INSERT: return ImGuiKey_Insert;\n        case SDLK_DELETE: return ImGuiKey_Delete;\n        case SDLK_BACKSPACE: return ImGuiKey_Backspace;\n        case SDLK_SPACE: return ImGuiKey_Space;\n        case SDLK_RETURN: return ImGuiKey_Enter;\n        case SDLK_ESCAPE: return ImGuiKey_Escape;\n        case SDLK_QUOTE: return ImGuiKey_Apostrophe;\n        case SDLK_COMMA: return ImGuiKey_Comma;\n        case SDLK_MINUS: return ImGuiKey_Minus;\n        case SDLK_PERIOD: return ImGuiKey_Period;\n        case SDLK_SLASH: return ImGuiKey_Slash;\n        case SDLK_SEMICOLON: return ImGuiKey_Semicolon;\n        case SDLK_EQUALS: return ImGuiKey_Equal;\n        case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;\n        case SDLK_BACKSLASH: return ImGuiKey_Backslash;\n        case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;\n        case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent;\n        case SDLK_CAPSLOCK: return ImGuiKey_CapsLock;\n        case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock;\n        case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock;\n        case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen;\n        case SDLK_PAUSE: return ImGuiKey_Pause;\n        case SDLK_KP_0: return ImGuiKey_Keypad0;\n        case SDLK_KP_1: return ImGuiKey_Keypad1;\n        case SDLK_KP_2: return ImGuiKey_Keypad2;\n        case SDLK_KP_3: return ImGuiKey_Keypad3;\n        case SDLK_KP_4: return ImGuiKey_Keypad4;\n        case SDLK_KP_5: return ImGuiKey_Keypad5;\n        case SDLK_KP_6: return ImGuiKey_Keypad6;\n        case SDLK_KP_7: return ImGuiKey_Keypad7;\n        case SDLK_KP_8: return ImGuiKey_Keypad8;\n        case SDLK_KP_9: return ImGuiKey_Keypad9;\n        case SDLK_KP_PERIOD: return ImGuiKey_KeypadDecimal;\n        case SDLK_KP_DIVIDE: return ImGuiKey_KeypadDivide;\n        case SDLK_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;\n        case SDLK_KP_MINUS: return ImGuiKey_KeypadSubtract;\n        case SDLK_KP_PLUS: return ImGuiKey_KeypadAdd;\n        case SDLK_KP_ENTER: return ImGuiKey_KeypadEnter;\n        case SDLK_KP_EQUALS: return ImGuiKey_KeypadEqual;\n        case SDLK_LCTRL: return ImGuiKey_LeftCtrl;\n        case SDLK_LSHIFT: return ImGuiKey_LeftShift;\n        case SDLK_LALT: return ImGuiKey_LeftAlt;\n        case SDLK_LGUI: return ImGuiKey_LeftSuper;\n        case SDLK_RCTRL: return ImGuiKey_RightCtrl;\n        case SDLK_RSHIFT: return ImGuiKey_RightShift;\n        case SDLK_RALT: return ImGuiKey_RightAlt;\n        case SDLK_RGUI: return ImGuiKey_RightSuper;\n        case SDLK_APPLICATION: return ImGuiKey_Menu;\n        case SDLK_0: return ImGuiKey_0;\n        case SDLK_1: return ImGuiKey_1;\n        case SDLK_2: return ImGuiKey_2;\n        case SDLK_3: return ImGuiKey_3;\n        case SDLK_4: return ImGuiKey_4;\n        case SDLK_5: return ImGuiKey_5;\n        case SDLK_6: return ImGuiKey_6;\n        case SDLK_7: return ImGuiKey_7;\n        case SDLK_8: return ImGuiKey_8;\n        case SDLK_9: return ImGuiKey_9;\n        case SDLK_a: return ImGuiKey_A;\n        case SDLK_b: return ImGuiKey_B;\n        case SDLK_c: return ImGuiKey_C;\n        case SDLK_d: return ImGuiKey_D;\n        case SDLK_e: return ImGuiKey_E;\n        case SDLK_f: return ImGuiKey_F;\n        case SDLK_g: return ImGuiKey_G;\n        case SDLK_h: return ImGuiKey_H;\n        case SDLK_i: return ImGuiKey_I;\n        case SDLK_j: return ImGuiKey_J;\n        case SDLK_k: return ImGuiKey_K;\n        case SDLK_l: return ImGuiKey_L;\n        case SDLK_m: return ImGuiKey_M;\n        case SDLK_n: return ImGuiKey_N;\n        case SDLK_o: return ImGuiKey_O;\n        case SDLK_p: return ImGuiKey_P;\n        case SDLK_q: return ImGuiKey_Q;\n        case SDLK_r: return ImGuiKey_R;\n        case SDLK_s: return ImGuiKey_S;\n        case SDLK_t: return ImGuiKey_T;\n        case SDLK_u: return ImGuiKey_U;\n        case SDLK_v: return ImGuiKey_V;\n        case SDLK_w: return ImGuiKey_W;\n        case SDLK_x: return ImGuiKey_X;\n        case SDLK_y: return ImGuiKey_Y;\n        case SDLK_z: return ImGuiKey_Z;\n        case SDLK_F1: return ImGuiKey_F1;\n        case SDLK_F2: return ImGuiKey_F2;\n        case SDLK_F3: return ImGuiKey_F3;\n        case SDLK_F4: return ImGuiKey_F4;\n        case SDLK_F5: return ImGuiKey_F5;\n        case SDLK_F6: return ImGuiKey_F6;\n        case SDLK_F7: return ImGuiKey_F7;\n        case SDLK_F8: return ImGuiKey_F8;\n        case SDLK_F9: return ImGuiKey_F9;\n        case SDLK_F10: return ImGuiKey_F10;\n        case SDLK_F11: return ImGuiKey_F11;\n        case SDLK_F12: return ImGuiKey_F12;\n        case SDLK_F13: return ImGuiKey_F13;\n        case SDLK_F14: return ImGuiKey_F14;\n        case SDLK_F15: return ImGuiKey_F15;\n        case SDLK_F16: return ImGuiKey_F16;\n        case SDLK_F17: return ImGuiKey_F17;\n        case SDLK_F18: return ImGuiKey_F18;\n        case SDLK_F19: return ImGuiKey_F19;\n        case SDLK_F20: return ImGuiKey_F20;\n        case SDLK_F21: return ImGuiKey_F21;\n        case SDLK_F22: return ImGuiKey_F22;\n        case SDLK_F23: return ImGuiKey_F23;\n        case SDLK_F24: return ImGuiKey_F24;\n        case SDLK_AC_BACK: return ImGuiKey_AppBack;\n        case SDLK_AC_FORWARD: return ImGuiKey_AppForward;\n    }\n    return ImGuiKey_None;\n}\n\nstatic void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)\n{\n    ImGuiIO& io = ImGui::GetIO();\n    io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & KMOD_CTRL) != 0);\n    io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & KMOD_SHIFT) != 0);\n    io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & KMOD_ALT) != 0);\n    io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & KMOD_GUI) != 0);\n}\n\n// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.\n// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.\n// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.\n// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.\n// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.\nbool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)\n{\n    ImGuiIO& io = ImGui::GetIO();\n    ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();\n\n    switch (event->type)\n    {\n        case SDL_MOUSEMOTION:\n        {\n            ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);\n            io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);\n            io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);\n            return true;\n        }\n        case SDL_MOUSEWHEEL:\n        {\n            //IMGUI_DEBUG_LOG(\"wheel %.2f %.2f, precise %.2f %.2f\\n\", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);\n#if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten!\n            float wheel_x = -event->wheel.preciseX;\n            float wheel_y = event->wheel.preciseY;\n#else\n            float wheel_x = -(float)event->wheel.x;\n            float wheel_y = (float)event->wheel.y;\n#endif\n#ifdef __EMSCRIPTEN__\n            wheel_x /= 100.0f;\n#endif\n            io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);\n            io.AddMouseWheelEvent(wheel_x, wheel_y);\n            return true;\n        }\n        case SDL_MOUSEBUTTONDOWN:\n        case SDL_MOUSEBUTTONUP:\n        {\n            int mouse_button = -1;\n            if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }\n            if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }\n            if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; }\n            if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; }\n            if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; }\n            if (mouse_button == -1)\n                break;\n            io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);\n            io.AddMouseButtonEvent(mouse_button, (event->type == SDL_MOUSEBUTTONDOWN));\n            bd->MouseButtonsDown = (event->type == SDL_MOUSEBUTTONDOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button));\n            return true;\n        }\n        case SDL_TEXTINPUT:\n        {\n            io.AddInputCharactersUTF8(event->text.text);\n            return true;\n        }\n        case SDL_KEYDOWN:\n        case SDL_KEYUP:\n        {\n            ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod);\n            ImGuiKey key = ImGui_ImplSDL2_KeycodeToImGuiKey(event->key.keysym.sym);\n            io.AddKeyEvent(key, (event->type == SDL_KEYDOWN));\n            io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.\n            return true;\n        }\n        case SDL_WINDOWEVENT:\n        {\n            // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right.\n            // - However we won't get a correct LEAVE event for a captured window.\n            // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,\n            //   causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why\n            //   we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details.\n            Uint8 window_event = event->window.event;\n            if (window_event == SDL_WINDOWEVENT_ENTER)\n            {\n                bd->MouseWindowID = event->window.windowID;\n                bd->PendingMouseLeaveFrame = 0;\n            }\n            if (window_event == SDL_WINDOWEVENT_LEAVE)\n                bd->PendingMouseLeaveFrame = ImGui::GetFrameCount() + 1;\n            if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED)\n                io.AddFocusEvent(true);\n            else if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST)\n                io.AddFocusEvent(false);\n            return true;\n        }\n    }\n    return false;\n}\n\nstatic bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer)\n{\n    ImGuiIO& io = ImGui::GetIO();\n    IM_ASSERT(io.BackendPlatformUserData == nullptr && \"Already initialized a platform backend!\");\n\n    // Check and store if we are on a SDL backend that supports global mouse position\n    // (\"wayland\" and \"rpi\" don't support it, but we chose to use a white-list instead of a black-list)\n    bool mouse_can_use_global_state = false;\n#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE\n    const char* sdl_backend = SDL_GetCurrentVideoDriver();\n    const char* global_mouse_whitelist[] = { \"windows\", \"cocoa\", \"x11\", \"DIVE\", \"VMAN\" };\n    for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++)\n        if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0)\n            mouse_can_use_global_state = true;\n#endif\n\n    // Setup backend capabilities flags\n    ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)();\n    io.BackendPlatformUserData = (void*)bd;\n    io.BackendPlatformName = \"imgui_impl_sdl2\";\n    io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;       // We can honor GetMouseCursor() values (optional)\n    io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;        // We can honor io.WantSetMousePos requests (optional, rarely used)\n\n    bd->Window = window;\n    bd->Renderer = renderer;\n    bd->MouseCanUseGlobalState = mouse_can_use_global_state;\n\n    io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;\n    io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;\n    io.ClipboardUserData = nullptr;\n    io.SetPlatformImeDataFn = ImGui_ImplSDL2_SetPlatformImeData;\n\n    // Load mouse cursors\n    bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);\n    bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);\n    bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);\n    bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);\n    bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);\n    bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);\n    bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);\n    bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);\n    bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);\n\n    // Set platform dependent data in viewport\n    // Our mouse update function expect PlatformHandle to be filled for the main viewport\n    ImGuiViewport* main_viewport = ImGui::GetMainViewport();\n    main_viewport->PlatformHandleRaw = nullptr;\n    SDL_SysWMinfo info;\n    SDL_VERSION(&info.version);\n    if (SDL_GetWindowWMInfo(window, &info))\n    {\n#if defined(SDL_VIDEO_DRIVER_WINDOWS)\n        main_viewport->PlatformHandleRaw = (void*)info.info.win.window;\n#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)\n        main_viewport->PlatformHandleRaw = (void*)info.info.cocoa.window;\n#endif\n    }\n\n    // From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.\n    // Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.\n    // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.\n    // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:\n    // you can ignore SDL_MOUSEBUTTONDOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)\n#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH\n    SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, \"1\");\n#endif\n\n    // From 2.0.18: Enable native IME.\n    // IMPORTANT: This is used at the time of SDL_CreateWindow() so this will only affects secondary windows, if any.\n    // For the main window to be affected, your application needs to call this manually before calling SDL_CreateWindow().\n#ifdef SDL_HINT_IME_SHOW_UI\n    SDL_SetHint(SDL_HINT_IME_SHOW_UI, \"1\");\n#endif\n\n    // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)\n#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE\n    SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, \"0\");\n#endif\n\n    return true;\n}\n\nbool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)\n{\n    IM_UNUSED(sdl_gl_context); // Viewport branch will need this.\n    return ImGui_ImplSDL2_Init(window, nullptr);\n}\n\nbool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)\n{\n#if !SDL_HAS_VULKAN\n    IM_ASSERT(0 && \"Unsupported\");\n#endif\n    return ImGui_ImplSDL2_Init(window, nullptr);\n}\n\nbool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)\n{\n#if !defined(_WIN32)\n    IM_ASSERT(0 && \"Unsupported\");\n#endif\n    return ImGui_ImplSDL2_Init(window, nullptr);\n}\n\nbool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)\n{\n    return ImGui_ImplSDL2_Init(window, nullptr);\n}\n\nbool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)\n{\n    return ImGui_ImplSDL2_Init(window, renderer);\n}\n\nbool ImGui_ImplSDL2_InitForOther(SDL_Window* window)\n{\n    return ImGui_ImplSDL2_Init(window, nullptr);\n}\n\nvoid ImGui_ImplSDL2_Shutdown()\n{\n    ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();\n    IM_ASSERT(bd != nullptr && \"No platform backend to shutdown, or already shutdown?\");\n    ImGuiIO& io = ImGui::GetIO();\n\n    if (bd->ClipboardTextData)\n        SDL_free(bd->ClipboardTextData);\n    for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)\n        SDL_FreeCursor(bd->MouseCursors[cursor_n]);\n    bd->LastMouseCursor = nullptr;\n\n    io.BackendPlatformName = nullptr;\n    io.BackendPlatformUserData = nullptr;\n    io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad);\n    IM_DELETE(bd);\n}\n\nstatic void ImGui_ImplSDL2_UpdateMouseData()\n{\n    ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();\n    ImGuiIO& io = ImGui::GetIO();\n\n    // We forward mouse input when hovered or captured (via SDL_MOUSEMOTION) or when focused (below)\n#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE\n    // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside\n    SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE);\n    SDL_Window* focused_window = SDL_GetKeyboardFocus();\n    const bool is_app_focused = (bd->Window == focused_window);\n#else\n    const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only\n#endif\n    if (is_app_focused)\n    {\n        // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)\n        if (io.WantSetMousePos)\n            SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y);\n\n        // (Optional) Fallback to provide mouse position when focused (SDL_MOUSEMOTION already provides this when hovered or captured)\n        if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0)\n        {\n            int window_x, window_y, mouse_x_global, mouse_y_global;\n            SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);\n            SDL_GetWindowPosition(bd->Window, &window_x, &window_y);\n            io.AddMousePosEvent((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y));\n        }\n    }\n}\n\nstatic void ImGui_ImplSDL2_UpdateMouseCursor()\n{\n    ImGuiIO& io = ImGui::GetIO();\n    if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)\n        return;\n    ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();\n\n    ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();\n    if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)\n    {\n        // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor\n        SDL_ShowCursor(SDL_FALSE);\n    }\n    else\n    {\n        // Show OS mouse cursor\n        SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow];\n        if (bd->LastMouseCursor != expected_cursor)\n        {\n            SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)\n            bd->LastMouseCursor = expected_cursor;\n        }\n        SDL_ShowCursor(SDL_TRUE);\n    }\n}\n\nstatic void ImGui_ImplSDL2_UpdateGamepads()\n{\n    ImGuiIO& io = ImGui::GetIO();\n    if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.\n        return;\n\n    // Get gamepad\n    io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;\n    SDL_GameController* game_controller = SDL_GameControllerOpen(0);\n    if (!game_controller)\n        return;\n    io.BackendFlags |= ImGuiBackendFlags_HasGamepad;\n\n    // Update gamepad inputs\n    #define IM_SATURATE(V)                      (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V)\n    #define MAP_BUTTON(KEY_NO, BUTTON_NO)       { io.AddKeyEvent(KEY_NO, SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0); }\n    #define MAP_ANALOG(KEY_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn); }\n    const int thumb_dead_zone = 8000;           // SDL_gamecontroller.h suggests using this value.\n    MAP_BUTTON(ImGuiKey_GamepadStart,           SDL_CONTROLLER_BUTTON_START);\n    MAP_BUTTON(ImGuiKey_GamepadBack,            SDL_CONTROLLER_BUTTON_BACK);\n    MAP_BUTTON(ImGuiKey_GamepadFaceLeft,        SDL_CONTROLLER_BUTTON_X);              // Xbox X, PS Square\n    MAP_BUTTON(ImGuiKey_GamepadFaceRight,       SDL_CONTROLLER_BUTTON_B);              // Xbox B, PS Circle\n    MAP_BUTTON(ImGuiKey_GamepadFaceUp,          SDL_CONTROLLER_BUTTON_Y);              // Xbox Y, PS Triangle\n    MAP_BUTTON(ImGuiKey_GamepadFaceDown,        SDL_CONTROLLER_BUTTON_A);              // Xbox A, PS Cross\n    MAP_BUTTON(ImGuiKey_GamepadDpadLeft,        SDL_CONTROLLER_BUTTON_DPAD_LEFT);\n    MAP_BUTTON(ImGuiKey_GamepadDpadRight,       SDL_CONTROLLER_BUTTON_DPAD_RIGHT);\n    MAP_BUTTON(ImGuiKey_GamepadDpadUp,          SDL_CONTROLLER_BUTTON_DPAD_UP);\n    MAP_BUTTON(ImGuiKey_GamepadDpadDown,        SDL_CONTROLLER_BUTTON_DPAD_DOWN);\n    MAP_BUTTON(ImGuiKey_GamepadL1,              SDL_CONTROLLER_BUTTON_LEFTSHOULDER);\n    MAP_BUTTON(ImGuiKey_GamepadR1,              SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);\n    MAP_ANALOG(ImGuiKey_GamepadL2,              SDL_CONTROLLER_AXIS_TRIGGERLEFT,  0.0f, 32767);\n    MAP_ANALOG(ImGuiKey_GamepadR2,              SDL_CONTROLLER_AXIS_TRIGGERRIGHT, 0.0f, 32767);\n    MAP_BUTTON(ImGuiKey_GamepadL3,              SDL_CONTROLLER_BUTTON_LEFTSTICK);\n    MAP_BUTTON(ImGuiKey_GamepadR3,              SDL_CONTROLLER_BUTTON_RIGHTSTICK);\n    MAP_ANALOG(ImGuiKey_GamepadLStickLeft,      SDL_CONTROLLER_AXIS_LEFTX,  -thumb_dead_zone, -32768);\n    MAP_ANALOG(ImGuiKey_GamepadLStickRight,     SDL_CONTROLLER_AXIS_LEFTX,  +thumb_dead_zone, +32767);\n    MAP_ANALOG(ImGuiKey_GamepadLStickUp,        SDL_CONTROLLER_AXIS_LEFTY,  -thumb_dead_zone, -32768);\n    MAP_ANALOG(ImGuiKey_GamepadLStickDown,      SDL_CONTROLLER_AXIS_LEFTY,  +thumb_dead_zone, +32767);\n    MAP_ANALOG(ImGuiKey_GamepadRStickLeft,      SDL_CONTROLLER_AXIS_RIGHTX, -thumb_dead_zone, -32768);\n    MAP_ANALOG(ImGuiKey_GamepadRStickRight,     SDL_CONTROLLER_AXIS_RIGHTX, +thumb_dead_zone, +32767);\n    MAP_ANALOG(ImGuiKey_GamepadRStickUp,        SDL_CONTROLLER_AXIS_RIGHTY, -thumb_dead_zone, -32768);\n    MAP_ANALOG(ImGuiKey_GamepadRStickDown,      SDL_CONTROLLER_AXIS_RIGHTY, +thumb_dead_zone, +32767);\n    #undef MAP_BUTTON\n    #undef MAP_ANALOG\n}\n\nvoid ImGui_ImplSDL2_NewFrame()\n{\n    ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();\n    IM_ASSERT(bd != nullptr && \"Did you call ImGui_ImplSDL2_Init()?\");\n    ImGuiIO& io = ImGui::GetIO();\n\n    // Setup display size (every frame to accommodate for window resizing)\n    int w, h;\n    int display_w, display_h;\n    SDL_GetWindowSize(bd->Window, &w, &h);\n    if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED)\n        w = h = 0;\n    if (bd->Renderer != nullptr)\n        SDL_GetRendererOutputSize(bd->Renderer, &display_w, &display_h);\n    else\n        SDL_GL_GetDrawableSize(bd->Window, &display_w, &display_h);\n    io.DisplaySize = ImVec2((float)w, (float)h);\n    if (w > 0 && h > 0)\n        io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);\n\n    // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)\n    // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644)\n    static Uint64 frequency = SDL_GetPerformanceFrequency();\n    Uint64 current_time = SDL_GetPerformanceCounter();\n    if (current_time <= bd->Time)\n        current_time = bd->Time + 1;\n    io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f);\n    bd->Time = current_time;\n\n    if (bd->PendingMouseLeaveFrame && bd->PendingMouseLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)\n    {\n        bd->MouseWindowID = 0;\n        bd->PendingMouseLeaveFrame = 0;\n        io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);\n    }\n\n    ImGui_ImplSDL2_UpdateMouseData();\n    ImGui_ImplSDL2_UpdateMouseCursor();\n\n    // Update game controllers (if enabled and available)\n    ImGui_ImplSDL2_UpdateGamepads();\n}\n\n//-----------------------------------------------------------------------------\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "third-party/imgui/imgui_impl_sdl2.h",
    "content": "// dear imgui: Platform Backend for SDL2\n// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)\n// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)\n\n// Implemented features:\n//  [X] Platform: Clipboard support.\n//  [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.\n//  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]\n//  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.\n//  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.\n//  [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, \"1\");' before SDL_CreateWindow()!.\n\n// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.\n// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.\n// Learn about Dear ImGui:\n// - FAQ                  https://dearimgui.com/faq\n// - Getting Started      https://dearimgui.com/getting-started\n// - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).\n// - Introduction, links and more at the top of imgui.cpp\n\n#pragma once\n#include \"imgui.h\"      // IMGUI_IMPL_API\n#ifndef IMGUI_DISABLE\n\nstruct SDL_Window;\nstruct SDL_Renderer;\ntypedef union SDL_Event SDL_Event;\n\nIMGUI_IMPL_API bool     ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);\nIMGUI_IMPL_API bool     ImGui_ImplSDL2_InitForVulkan(SDL_Window* window);\nIMGUI_IMPL_API bool     ImGui_ImplSDL2_InitForD3D(SDL_Window* window);\nIMGUI_IMPL_API bool     ImGui_ImplSDL2_InitForMetal(SDL_Window* window);\nIMGUI_IMPL_API bool     ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer);\nIMGUI_IMPL_API bool     ImGui_ImplSDL2_InitForOther(SDL_Window* window);\nIMGUI_IMPL_API void     ImGui_ImplSDL2_Shutdown();\nIMGUI_IMPL_API void     ImGui_ImplSDL2_NewFrame();\nIMGUI_IMPL_API bool     ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\nstatic inline void ImGui_ImplSDL2_NewFrame(SDL_Window*) { ImGui_ImplSDL2_NewFrame(); } // 1.84: removed unnecessary parameter\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "third-party/imgui/imgui_impl_sdlrenderer2.cpp",
    "content": "// dear imgui: Renderer Backend for SDL_Renderer for SDL2\n// (Requires: SDL 2.0.17+)\n\n// Note how SDL_Renderer is an _optional_ component of SDL2.\n// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX.\n// If your application will want to render any non trivial amount of graphics other than UI,\n// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and\n// it might be difficult to step out of those boundaries.\n\n// Implemented features:\n//  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!\n//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.\n\n// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.\n// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.\n// Learn about Dear ImGui:\n// - FAQ                  https://dearimgui.com/faq\n// - Getting Started      https://dearimgui.com/getting-started\n// - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).\n// - Introduction, links and more at the top of imgui.cpp\n\n// CHANGELOG\n//  2023-05-30: Renamed imgui_impl_sdlrenderer.h/.cpp to imgui_impl_sdlrenderer2.h/.cpp to accommodate for upcoming SDL3.\n//  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.\n//  2021-12-21: Update SDL_RenderGeometryRaw() format to work with SDL 2.0.19.\n//  2021-12-03: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.\n//  2021-10-06: Backup and restore modified ClipRect/Viewport.\n//  2021-09-21: Initial version.\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_impl_sdlrenderer2.h\"\n#include <stdint.h>     // intptr_t\n\n// Clang warnings with -Weverything\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wsign-conversion\"    // warning: implicit conversion changes signedness\n#endif\n\n// SDL\n#include <SDL.h>\n#if !SDL_VERSION_ATLEAST(2,0,17)\n#error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function\n#endif\n\n// SDL_Renderer data\nstruct ImGui_ImplSDLRenderer2_Data\n{\n    SDL_Renderer*   SDLRenderer;\n    SDL_Texture*    FontTexture;\n    ImGui_ImplSDLRenderer2_Data() { memset((void*)this, 0, sizeof(*this)); }\n};\n\n// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts\n// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.\nstatic ImGui_ImplSDLRenderer2_Data* ImGui_ImplSDLRenderer2_GetBackendData()\n{\n    return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer2_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;\n}\n\n// Functions\nbool ImGui_ImplSDLRenderer2_Init(SDL_Renderer* renderer)\n{\n    ImGuiIO& io = ImGui::GetIO();\n    IM_ASSERT(io.BackendRendererUserData == nullptr && \"Already initialized a renderer backend!\");\n    IM_ASSERT(renderer != nullptr && \"SDL_Renderer not initialized!\");\n\n    // Setup backend capabilities flags\n    ImGui_ImplSDLRenderer2_Data* bd = IM_NEW(ImGui_ImplSDLRenderer2_Data)();\n    io.BackendRendererUserData = (void*)bd;\n    io.BackendRendererName = \"imgui_impl_sdlrenderer2\";\n    io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.\n\n    bd->SDLRenderer = renderer;\n\n    return true;\n}\n\nvoid ImGui_ImplSDLRenderer2_Shutdown()\n{\n    ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();\n    IM_ASSERT(bd != nullptr && \"No renderer backend to shutdown, or already shutdown?\");\n    ImGuiIO& io = ImGui::GetIO();\n\n    ImGui_ImplSDLRenderer2_DestroyDeviceObjects();\n\n    io.BackendRendererName = nullptr;\n    io.BackendRendererUserData = nullptr;\n    io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;\n    IM_DELETE(bd);\n}\n\nstatic void ImGui_ImplSDLRenderer2_SetupRenderState()\n{\n\tImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();\n\n\t// Clear out any viewports and cliprect set by the user\n    // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process.\n\tSDL_RenderSetViewport(bd->SDLRenderer, nullptr);\n\tSDL_RenderSetClipRect(bd->SDLRenderer, nullptr);\n}\n\nvoid ImGui_ImplSDLRenderer2_NewFrame()\n{\n    ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();\n    IM_ASSERT(bd != nullptr && \"Did you call ImGui_ImplSDLRenderer2_Init()?\");\n\n    if (!bd->FontTexture)\n        ImGui_ImplSDLRenderer2_CreateDeviceObjects();\n}\n\nvoid ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data)\n{\n\tImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();\n\n\t// If there's a scale factor set by the user, use that instead\n    // If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass\n    // to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here.\n    float rsx = 1.0f;\n\tfloat rsy = 1.0f;\n\tSDL_RenderGetScale(bd->SDLRenderer, &rsx, &rsy);\n    ImVec2 render_scale;\n\trender_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f;\n\trender_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f;\n\n\t// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)\n\tint fb_width = (int)(draw_data->DisplaySize.x * render_scale.x);\n\tint fb_height = (int)(draw_data->DisplaySize.y * render_scale.y);\n\tif (fb_width == 0 || fb_height == 0)\n\t\treturn;\n\n    // Backup SDL_Renderer state that will be modified to restore it afterwards\n    struct BackupSDLRendererState\n    {\n        SDL_Rect    Viewport;\n        bool        ClipEnabled;\n        SDL_Rect    ClipRect;\n    };\n    BackupSDLRendererState old = {};\n    old.ClipEnabled = SDL_RenderIsClipEnabled(bd->SDLRenderer) == SDL_TRUE;\n    SDL_RenderGetViewport(bd->SDLRenderer, &old.Viewport);\n    SDL_RenderGetClipRect(bd->SDLRenderer, &old.ClipRect);\n\n\t// Will project scissor/clipping rectangles into framebuffer space\n\tImVec2 clip_off = draw_data->DisplayPos;         // (0,0) unless using multi-viewports\n\tImVec2 clip_scale = render_scale;\n\n    // Render command lists\n    ImGui_ImplSDLRenderer2_SetupRenderState();\n    for (int n = 0; n < draw_data->CmdListsCount; n++)\n    {\n        const ImDrawList* cmd_list = draw_data->CmdLists[n];\n        const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;\n        const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;\n\n        for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)\n        {\n            const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];\n            if (pcmd->UserCallback)\n            {\n                // User callback, registered via ImDrawList::AddCallback()\n                // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)\n                if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)\n                    ImGui_ImplSDLRenderer2_SetupRenderState();\n                else\n                    pcmd->UserCallback(cmd_list, pcmd);\n            }\n            else\n            {\n                // Project scissor/clipping rectangles into framebuffer space\n                ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);\n                ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);\n                if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }\n                if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }\n                if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; }\n                if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; }\n                if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)\n                    continue;\n\n                SDL_Rect r = { (int)(clip_min.x), (int)(clip_min.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y) };\n                SDL_RenderSetClipRect(bd->SDLRenderer, &r);\n\n                const float* xy = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, pos));\n                const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv));\n#if SDL_VERSION_ATLEAST(2,0,19)\n                const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.19+\n#else\n                const int* color = (const int*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); // SDL 2.0.17 and 2.0.18\n#endif\n\n                // Bind texture, Draw\n\t\t\t\tSDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID();\n                SDL_RenderGeometryRaw(bd->SDLRenderer, tex,\n                    xy, (int)sizeof(ImDrawVert),\n                    color, (int)sizeof(ImDrawVert),\n                    uv, (int)sizeof(ImDrawVert),\n                    cmd_list->VtxBuffer.Size - pcmd->VtxOffset,\n                    idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx));\n            }\n        }\n    }\n\n    // Restore modified SDL_Renderer state\n    SDL_RenderSetViewport(bd->SDLRenderer, &old.Viewport);\n    SDL_RenderSetClipRect(bd->SDLRenderer, old.ClipEnabled ? &old.ClipRect : nullptr);\n}\n\n// Called by Init/NewFrame/Shutdown\nbool ImGui_ImplSDLRenderer2_CreateFontsTexture()\n{\n    ImGuiIO& io = ImGui::GetIO();\n    ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();\n\n    // Build texture atlas\n    unsigned char* pixels;\n    int width, height;\n    io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);   // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.\n\n    // Upload texture to graphics system\n    // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)\n    bd->FontTexture = SDL_CreateTexture(bd->SDLRenderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height);\n    if (bd->FontTexture == nullptr)\n    {\n        SDL_Log(\"error creating texture\");\n        return false;\n    }\n    SDL_UpdateTexture(bd->FontTexture, nullptr, pixels, 4 * width);\n    SDL_SetTextureBlendMode(bd->FontTexture, SDL_BLENDMODE_BLEND);\n    SDL_SetTextureScaleMode(bd->FontTexture, SDL_ScaleModeLinear);\n\n    // Store our identifier\n    io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);\n\n    return true;\n}\n\nvoid ImGui_ImplSDLRenderer2_DestroyFontsTexture()\n{\n    ImGuiIO& io = ImGui::GetIO();\n    ImGui_ImplSDLRenderer2_Data* bd = ImGui_ImplSDLRenderer2_GetBackendData();\n    if (bd->FontTexture)\n    {\n        io.Fonts->SetTexID(0);\n        SDL_DestroyTexture(bd->FontTexture);\n        bd->FontTexture = nullptr;\n    }\n}\n\nbool ImGui_ImplSDLRenderer2_CreateDeviceObjects()\n{\n    return ImGui_ImplSDLRenderer2_CreateFontsTexture();\n}\n\nvoid ImGui_ImplSDLRenderer2_DestroyDeviceObjects()\n{\n    ImGui_ImplSDLRenderer2_DestroyFontsTexture();\n}\n\n//-----------------------------------------------------------------------------\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "third-party/imgui/imgui_impl_sdlrenderer2.h",
    "content": "// dear imgui: Renderer Backend for SDL_Renderer for SDL2\n// (Requires: SDL 2.0.17+)\n\n// Note how SDL_Renderer is an _optional_ component of SDL2.\n// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX.\n// If your application will want to render any non trivial amount of graphics other than UI,\n// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and\n// it might be difficult to step out of those boundaries.\n\n// Implemented features:\n//  [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!\n//  [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.\n\n// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.\n// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.\n// Learn about Dear ImGui:\n// - FAQ                  https://dearimgui.com/faq\n// - Getting Started      https://dearimgui.com/getting-started\n// - Documentation        https://dearimgui.com/docs (same as your local docs/ folder).\n// - Introduction, links and more at the top of imgui.cpp\n\n#pragma once\n#ifndef IMGUI_DISABLE\n#include \"imgui.h\"      // IMGUI_IMPL_API\n\nstruct SDL_Renderer;\n\nIMGUI_IMPL_API bool     ImGui_ImplSDLRenderer2_Init(SDL_Renderer* renderer);\nIMGUI_IMPL_API void     ImGui_ImplSDLRenderer2_Shutdown();\nIMGUI_IMPL_API void     ImGui_ImplSDLRenderer2_NewFrame();\nIMGUI_IMPL_API void     ImGui_ImplSDLRenderer2_RenderDrawData(ImDrawData* draw_data);\n\n// Called by Init/NewFrame/Shutdown\nIMGUI_IMPL_API bool     ImGui_ImplSDLRenderer2_CreateFontsTexture();\nIMGUI_IMPL_API void     ImGui_ImplSDLRenderer2_DestroyFontsTexture();\nIMGUI_IMPL_API bool     ImGui_ImplSDLRenderer2_CreateDeviceObjects();\nIMGUI_IMPL_API void     ImGui_ImplSDLRenderer2_DestroyDeviceObjects();\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "third-party/imgui/imgui_internal.h",
    "content": "// dear imgui, v1.90.0\n// (internal structures/api)\n\n// You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility.\n\n/*\n\nIndex of this file:\n\n// [SECTION] Header mess\n// [SECTION] Forward declarations\n// [SECTION] Context pointer\n// [SECTION] STB libraries includes\n// [SECTION] Macros\n// [SECTION] Generic helpers\n// [SECTION] ImDrawList support\n// [SECTION] Widgets support: flags, enums, data structures\n// [SECTION] Inputs support\n// [SECTION] Clipper support\n// [SECTION] Navigation support\n// [SECTION] Typing-select support\n// [SECTION] Columns support\n// [SECTION] Multi-select support\n// [SECTION] Docking support\n// [SECTION] Viewport support\n// [SECTION] Settings support\n// [SECTION] Localization support\n// [SECTION] Metrics, Debug tools\n// [SECTION] Generic context hooks\n// [SECTION] ImGuiContext (main imgui context)\n// [SECTION] ImGuiWindowTempData, ImGuiWindow\n// [SECTION] Tab bar, Tab item support\n// [SECTION] Table support\n// [SECTION] ImGui internal API\n// [SECTION] ImFontAtlas internal API\n// [SECTION] Test Engine specific hooks (imgui_test_engine)\n\n*/\n\n#pragma once\n#ifndef IMGUI_DISABLE\n\n//-----------------------------------------------------------------------------\n// [SECTION] Header mess\n//-----------------------------------------------------------------------------\n\n#ifndef IMGUI_VERSION\n#include \"imgui.h\"\n#endif\n\n#include <stdio.h>      // FILE*, sscanf\n#include <stdlib.h>     // NULL, malloc, free, qsort, atoi, atof\n#include <math.h>       // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf\n#include <limits.h>     // INT_MIN, INT_MAX\n\n// Enable SSE intrinsics if available\n#if (defined __SSE__ || defined __x86_64__ || defined _M_X64 || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))) && !defined(IMGUI_DISABLE_SSE)\n#define IMGUI_ENABLE_SSE\n#include <immintrin.h>\n#endif\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (push)\n#pragma warning (disable: 4251)     // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport)\n#pragma warning (disable: 26812)    // The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer)\n#pragma warning (disable: 26495)    // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).\n#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later\n#pragma warning (disable: 5054)     // operator '|': deprecated between enumerations of different types\n#endif\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#pragma clang diagnostic push\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wfloat-equal\"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok, for ImFloor()\n#pragma clang diagnostic ignored \"-Wunused-function\"                // for stb_textedit.h\n#pragma clang diagnostic ignored \"-Wmissing-prototypes\"             // for stb_textedit.h\n#pragma clang diagnostic ignored \"-Wold-style-cast\"\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#pragma clang diagnostic ignored \"-Wmissing-noreturn\"               // warning: function 'xxx' could be declared with attribute 'noreturn'\n#elif defined(__GNUC__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpragmas\"              // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"      // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#endif\n\n// In 1.89.4, we moved the implementation of \"courtesy maths operators\" from imgui_internal.h in imgui.h\n// As they are frequently requested, we do not want to encourage to many people using imgui_internal.h\n#if defined(IMGUI_DEFINE_MATH_OPERATORS) && !defined(IMGUI_DEFINE_MATH_OPERATORS_IMPLEMENTED)\n#error Please '#define IMGUI_DEFINE_MATH_OPERATORS' _BEFORE_ including imgui.h!\n#endif\n\n// Legacy defines\n#ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS            // Renamed in 1.74\n#error Use IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n#endif\n#ifdef IMGUI_DISABLE_MATH_FUNCTIONS                     // Renamed in 1.74\n#error Use IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS\n#endif\n\n// Enable stb_truetype by default unless FreeType is enabled.\n// You can compile with both by defining both IMGUI_ENABLE_FREETYPE and IMGUI_ENABLE_STB_TRUETYPE together.\n#ifndef IMGUI_ENABLE_FREETYPE\n#define IMGUI_ENABLE_STB_TRUETYPE\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Forward declarations\n//-----------------------------------------------------------------------------\n\nstruct ImBitVector;                 // Store 1-bit per value\nstruct ImRect;                      // An axis-aligned rectangle (2 points)\nstruct ImDrawDataBuilder;           // Helper to build a ImDrawData instance\nstruct ImDrawListSharedData;        // Data shared between all ImDrawList instances\nstruct ImGuiColorMod;               // Stacked color modifier, backup of modified data so we can restore it\nstruct ImGuiContext;                // Main Dear ImGui context\nstruct ImGuiContextHook;            // Hook for extensions like ImGuiTestEngine\nstruct ImGuiDataVarInfo;            // Variable information (e.g. to avoid style variables from an enum)\nstruct ImGuiDataTypeInfo;           // Type information associated to a ImGuiDataType enum\nstruct ImGuiGroupData;              // Stacked storage data for BeginGroup()/EndGroup()\nstruct ImGuiInputTextState;         // Internal state of the currently focused/edited text input box\nstruct ImGuiInputTextDeactivateData;// Short term storage to backup text of a deactivating InputText() while another is stealing active id\nstruct ImGuiLastItemData;           // Status storage for last submitted items\nstruct ImGuiLocEntry;               // A localization entry.\nstruct ImGuiMenuColumns;            // Simple column measurement, currently used for MenuItem() only\nstruct ImGuiNavItemData;            // Result of a gamepad/keyboard directional navigation move query result\nstruct ImGuiNavTreeNodeData;        // Temporary storage for last TreeNode() being a Left arrow landing candidate.\nstruct ImGuiMetricsConfig;          // Storage for ShowMetricsWindow() and DebugNodeXXX() functions\nstruct ImGuiNextWindowData;         // Storage for SetNextWindow** functions\nstruct ImGuiNextItemData;           // Storage for SetNextItem** functions\nstruct ImGuiOldColumnData;          // Storage data for a single column for legacy Columns() api\nstruct ImGuiOldColumns;             // Storage data for a columns set for legacy Columns() api\nstruct ImGuiPopupData;              // Storage for current popup stack\nstruct ImGuiSettingsHandler;        // Storage for one type registered in the .ini file\nstruct ImGuiStackSizes;             // Storage of stack sizes for debugging/asserting\nstruct ImGuiStyleMod;               // Stacked style modifier, backup of modified data so we can restore it\nstruct ImGuiTabBar;                 // Storage for a tab bar\nstruct ImGuiTabItem;                // Storage for a tab item (within a tab bar)\nstruct ImGuiTable;                  // Storage for a table\nstruct ImGuiTableColumn;            // Storage for one column of a table\nstruct ImGuiTableInstanceData;      // Storage for one instance of a same table\nstruct ImGuiTableTempData;          // Temporary storage for one table (one per table in the stack), shared between tables.\nstruct ImGuiTableSettings;          // Storage for a table .ini settings\nstruct ImGuiTableColumnsSettings;   // Storage for a column .ini settings\nstruct ImGuiTypingSelectState;      // Storage for GetTypingSelectRequest()\nstruct ImGuiTypingSelectRequest;    // Storage for GetTypingSelectRequest() (aimed to be public)\nstruct ImGuiWindow;                 // Storage for one window\nstruct ImGuiWindowTempData;         // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window)\nstruct ImGuiWindowSettings;         // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session)\n\n// Enumerations\n// Use your programming IDE \"Go to definition\" facility on the names of the center columns to find the actual flags/enum lists.\nenum ImGuiLocKey : int;                 // -> enum ImGuiLocKey              // Enum: a localization entry for translation.\ntypedef int ImGuiLayoutType;            // -> enum ImGuiLayoutType_         // Enum: Horizontal or vertical\n\n// Flags\ntypedef int ImGuiActivateFlags;         // -> enum ImGuiActivateFlags_      // Flags: for navigation/focus function (will be for ActivateItem() later)\ntypedef int ImGuiDebugLogFlags;         // -> enum ImGuiDebugLogFlags_      // Flags: for ShowDebugLogWindow(), g.DebugLogFlags\ntypedef int ImGuiFocusRequestFlags;     // -> enum ImGuiFocusRequestFlags_  // Flags: for FocusWindow();\ntypedef int ImGuiInputFlags;            // -> enum ImGuiInputFlags_         // Flags: for IsKeyPressed(), IsMouseClicked(), SetKeyOwner(), SetItemKeyOwner() etc.\ntypedef int ImGuiItemFlags;             // -> enum ImGuiItemFlags_          // Flags: for PushItemFlag(), g.LastItemData.InFlags\ntypedef int ImGuiItemStatusFlags;       // -> enum ImGuiItemStatusFlags_    // Flags: for g.LastItemData.StatusFlags\ntypedef int ImGuiOldColumnFlags;        // -> enum ImGuiOldColumnFlags_     // Flags: for BeginColumns()\ntypedef int ImGuiNavHighlightFlags;     // -> enum ImGuiNavHighlightFlags_  // Flags: for RenderNavHighlight()\ntypedef int ImGuiNavMoveFlags;          // -> enum ImGuiNavMoveFlags_       // Flags: for navigation requests\ntypedef int ImGuiNextItemDataFlags;     // -> enum ImGuiNextItemDataFlags_  // Flags: for SetNextItemXXX() functions\ntypedef int ImGuiNextWindowDataFlags;   // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions\ntypedef int ImGuiScrollFlags;           // -> enum ImGuiScrollFlags_        // Flags: for ScrollToItem() and navigation requests\ntypedef int ImGuiSeparatorFlags;        // -> enum ImGuiSeparatorFlags_     // Flags: for SeparatorEx()\ntypedef int ImGuiTextFlags;             // -> enum ImGuiTextFlags_          // Flags: for TextEx()\ntypedef int ImGuiTooltipFlags;          // -> enum ImGuiTooltipFlags_       // Flags: for BeginTooltipEx()\ntypedef int ImGuiTypingSelectFlags;     // -> enum ImGuiTypingSelectFlags_  // Flags: for GetTypingSelectRequest()\n\ntypedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Context pointer\n// See implementation of this variable in imgui.cpp for comments and details.\n//-----------------------------------------------------------------------------\n\n#ifndef GImGui\nextern IMGUI_API ImGuiContext* GImGui;  // Current implicit context pointer\n#endif\n\n//-------------------------------------------------------------------------\n// [SECTION] STB libraries includes\n//-------------------------------------------------------------------------\n\nnamespace ImStb\n{\n\n#undef STB_TEXTEDIT_STRING\n#undef STB_TEXTEDIT_CHARTYPE\n#define STB_TEXTEDIT_STRING             ImGuiInputTextState\n#define STB_TEXTEDIT_CHARTYPE           ImWchar\n#define STB_TEXTEDIT_GETWIDTH_NEWLINE   (-1.0f)\n#define STB_TEXTEDIT_UNDOSTATECOUNT     99\n#define STB_TEXTEDIT_UNDOCHARCOUNT      999\n#include \"imstb_textedit.h\"\n\n} // namespace ImStb\n\n//-----------------------------------------------------------------------------\n// [SECTION] Macros\n//-----------------------------------------------------------------------------\n\n// Debug Printing Into TTY\n// (since IMGUI_VERSION_NUM >= 18729: IMGUI_DEBUG_LOG was reworked into IMGUI_DEBUG_PRINTF (and removed framecount from it). If you were using a #define IMGUI_DEBUG_LOG please rename)\n#ifndef IMGUI_DEBUG_PRINTF\n#ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS\n#define IMGUI_DEBUG_PRINTF(_FMT,...)    printf(_FMT, __VA_ARGS__)\n#else\n#define IMGUI_DEBUG_PRINTF(_FMT,...)    ((void)0)\n#endif\n#endif\n\n// Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam.\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n#define IMGUI_DEBUG_LOG(...)            ImGui::DebugLog(__VA_ARGS__)\n#else\n#define IMGUI_DEBUG_LOG(...)            ((void)0)\n#endif\n#define IMGUI_DEBUG_LOG_ACTIVEID(...)   do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_FOCUS(...)      do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus)    IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_POPUP(...)      do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup)    IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_NAV(...)        do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav)      IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_SELECTION(...)  do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_CLIPPER(...)    do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper)  IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n#define IMGUI_DEBUG_LOG_IO(...)         do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO)       IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0)\n\n// Static Asserts\n#define IM_STATIC_ASSERT(_COND)         static_assert(_COND, \"\")\n\n// \"Paranoid\" Debug Asserts are meant to only be enabled during specific debugging/work, otherwise would slow down the code too much.\n// We currently don't have many of those so the effect is currently negligible, but onward intent to add more aggressive ones in the code.\n//#define IMGUI_DEBUG_PARANOID\n#ifdef IMGUI_DEBUG_PARANOID\n#define IM_ASSERT_PARANOID(_EXPR)       IM_ASSERT(_EXPR)\n#else\n#define IM_ASSERT_PARANOID(_EXPR)\n#endif\n\n// Error handling\n// Down the line in some frameworks/languages we would like to have a way to redirect those to the programmer and recover from more faults.\n#ifndef IM_ASSERT_USER_ERROR\n#define IM_ASSERT_USER_ERROR(_EXP,_MSG) IM_ASSERT((_EXP) && _MSG)   // Recoverable User Error\n#endif\n\n// Misc Macros\n#define IM_PI                           3.14159265358979323846f\n#ifdef _WIN32\n#define IM_NEWLINE                      \"\\r\\n\"   // Play it nice with Windows users (Update: since 2018-05, Notepad finally appears to support Unix-style carriage returns!)\n#else\n#define IM_NEWLINE                      \"\\n\"\n#endif\n#ifndef IM_TABSIZE                      // Until we move this to runtime and/or add proper tab support, at least allow users to compile-time override\n#define IM_TABSIZE                      (4)\n#endif\n#define IM_MEMALIGN(_OFF,_ALIGN)        (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1))           // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8\n#define IM_F32_TO_INT8_UNBOUND(_VAL)    ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f)))   // Unsaturated, for display purpose\n#define IM_F32_TO_INT8_SAT(_VAL)        ((int)(ImSaturate(_VAL) * 255.0f + 0.5f))               // Saturated, always output 0..255\n#define IM_TRUNC(_VAL)                  ((float)(int)(_VAL))                                    // ImTrunc() is not inlined in MSVC debug builds\n#define IM_ROUND(_VAL)                  ((float)(int)((_VAL) + 0.5f))                           //\n#define IM_STRINGIFY_HELPER(_X)         #_X\n#define IM_STRINGIFY(_X)                IM_STRINGIFY_HELPER(_X)                                 // Preprocessor idiom to stringify e.g. an integer.\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n#define IM_FLOOR IM_TRUNC\n#endif\n\n// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall\n#ifdef _MSC_VER\n#define IMGUI_CDECL __cdecl\n#else\n#define IMGUI_CDECL\n#endif\n\n// Warnings\n#if defined(_MSC_VER) && !defined(__clang__)\n#define IM_MSVC_WARNING_SUPPRESS(XXXX)  __pragma(warning(suppress: XXXX))\n#else\n#define IM_MSVC_WARNING_SUPPRESS(XXXX)\n#endif\n\n// Debug Tools\n// Use 'Metrics/Debugger->Tools->Item Picker' to break into the call-stack of a specific item.\n// This will call IM_DEBUG_BREAK() which you may redefine yourself. See https://github.com/scottt/debugbreak for more reference.\n#ifndef IM_DEBUG_BREAK\n#if defined (_MSC_VER)\n#define IM_DEBUG_BREAK()    __debugbreak()\n#elif defined(__clang__)\n#define IM_DEBUG_BREAK()    __builtin_debugtrap()\n#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))\n#define IM_DEBUG_BREAK()    __asm__ volatile(\"int $0x03\")\n#elif defined(__GNUC__) && defined(__thumb__)\n#define IM_DEBUG_BREAK()    __asm__ volatile(\".inst 0xde01\")\n#elif defined(__GNUC__) && defined(__arm__) && !defined(__thumb__)\n#define IM_DEBUG_BREAK()    __asm__ volatile(\".inst 0xe7f001f0\");\n#else\n#define IM_DEBUG_BREAK()    IM_ASSERT(0)    // It is expected that you define IM_DEBUG_BREAK() into something that will break nicely in a debugger!\n#endif\n#endif // #ifndef IM_DEBUG_BREAK\n\n// Format specifiers, printing 64-bit hasn't been decently standardized...\n// In a real application you should be using PRId64 and PRIu64 from <inttypes.h> (non-windows) and on Windows define them yourself.\n#if defined(_MSC_VER) && !defined(__clang__)\n#define IM_PRId64   \"I64d\"\n#define IM_PRIu64   \"I64u\"\n#define IM_PRIX64   \"I64X\"\n#else\n#define IM_PRId64   \"lld\"\n#define IM_PRIu64   \"llu\"\n#define IM_PRIX64   \"llX\"\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Generic helpers\n// Note that the ImXXX helpers functions are lower-level than ImGui functions.\n// ImGui functions or the ImGui context are never called/used from other ImXXX functions.\n//-----------------------------------------------------------------------------\n// - Helpers: Hashing\n// - Helpers: Sorting\n// - Helpers: Bit manipulation\n// - Helpers: String\n// - Helpers: Formatting\n// - Helpers: UTF-8 <> wchar conversions\n// - Helpers: ImVec2/ImVec4 operators\n// - Helpers: Maths\n// - Helpers: Geometry\n// - Helper: ImVec1\n// - Helper: ImVec2ih\n// - Helper: ImRect\n// - Helper: ImBitArray\n// - Helper: ImBitVector\n// - Helper: ImSpan<>, ImSpanAllocator<>\n// - Helper: ImPool<>\n// - Helper: ImChunkStream<>\n// - Helper: ImGuiTextIndex\n//-----------------------------------------------------------------------------\n\n// Helpers: Hashing\nIMGUI_API ImGuiID       ImHashData(const void* data, size_t data_size, ImGuiID seed = 0);\nIMGUI_API ImGuiID       ImHashStr(const char* data, size_t data_size = 0, ImGuiID seed = 0);\n\n// Helpers: Sorting\n#ifndef ImQsort\nstatic inline void      ImQsort(void* base, size_t count, size_t size_of_element, int(IMGUI_CDECL *compare_func)(void const*, void const*)) { if (count > 1) qsort(base, count, size_of_element, compare_func); }\n#endif\n\n// Helpers: Color Blending\nIMGUI_API ImU32         ImAlphaBlendColors(ImU32 col_a, ImU32 col_b);\n\n// Helpers: Bit manipulation\nstatic inline bool      ImIsPowerOfTwo(int v)           { return v != 0 && (v & (v - 1)) == 0; }\nstatic inline bool      ImIsPowerOfTwo(ImU64 v)         { return v != 0 && (v & (v - 1)) == 0; }\nstatic inline int       ImUpperPowerOfTwo(int v)        { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }\n\n// Helpers: String\nIMGUI_API int           ImStricmp(const char* str1, const char* str2);                      // Case insensitive compare.\nIMGUI_API int           ImStrnicmp(const char* str1, const char* str2, size_t count);       // Case insensitive compare to a certain count.\nIMGUI_API void          ImStrncpy(char* dst, const char* src, size_t count);                // Copy to a certain count and always zero terminate (strncpy doesn't).\nIMGUI_API char*         ImStrdup(const char* str);                                          // Duplicate a string.\nIMGUI_API char*         ImStrdupcpy(char* dst, size_t* p_dst_size, const char* str);        // Copy in provided buffer, recreate buffer if needed.\nIMGUI_API const char*   ImStrchrRange(const char* str_begin, const char* str_end, char c);  // Find first occurrence of 'c' in string range.\nIMGUI_API const char*   ImStreolRange(const char* str, const char* str_end);                // End end-of-line\nIMGUI_API const char*   ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end);  // Find a substring in a string range.\nIMGUI_API void          ImStrTrimBlanks(char* str);                                         // Remove leading and trailing blanks from a buffer.\nIMGUI_API const char*   ImStrSkipBlank(const char* str);                                    // Find first non-blank character.\nIMGUI_API int           ImStrlenW(const ImWchar* str);                                      // Computer string length (ImWchar string)\nIMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin);   // Find beginning-of-line (ImWchar string)\nIM_MSVC_RUNTIME_CHECKS_OFF\nstatic inline char      ImToUpper(char c)               { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; }\nstatic inline bool      ImCharIsBlankA(char c)          { return c == ' ' || c == '\\t'; }\nstatic inline bool      ImCharIsBlankW(unsigned int c)  { return c == ' ' || c == '\\t' || c == 0x3000; }\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n// Helpers: Formatting\nIMGUI_API int           ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3);\nIMGUI_API int           ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3);\nIMGUI_API void          ImFormatStringToTempBuffer(const char** out_buf, const char** out_buf_end, const char* fmt, ...) IM_FMTARGS(3);\nIMGUI_API void          ImFormatStringToTempBufferV(const char** out_buf, const char** out_buf_end, const char* fmt, va_list args) IM_FMTLIST(3);\nIMGUI_API const char*   ImParseFormatFindStart(const char* format);\nIMGUI_API const char*   ImParseFormatFindEnd(const char* format);\nIMGUI_API const char*   ImParseFormatTrimDecorations(const char* format, char* buf, size_t buf_size);\nIMGUI_API void          ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size);\nIMGUI_API const char*   ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size);\nIMGUI_API int           ImParseFormatPrecision(const char* format, int default_value);\n\n// Helpers: UTF-8 <> wchar conversions\nIMGUI_API const char*   ImTextCharToUtf8(char out_buf[5], unsigned int c);                                                      // return out_buf\nIMGUI_API int           ImTextStrToUtf8(char* out_buf, int out_buf_size, const ImWchar* in_text, const ImWchar* in_text_end);   // return output UTF-8 bytes count\nIMGUI_API int           ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end);               // read one character. return input UTF-8 bytes count\nIMGUI_API int           ImTextStrFromUtf8(ImWchar* out_buf, int out_buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL);   // return input UTF-8 bytes count\nIMGUI_API int           ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end);                                 // return number of UTF-8 code-points (NOT bytes count)\nIMGUI_API int           ImTextCountUtf8BytesFromChar(const char* in_text, const char* in_text_end);                             // return number of bytes to express one char in UTF-8\nIMGUI_API int           ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end);                        // return number of bytes to express string in UTF-8\nIMGUI_API const char*   ImTextFindPreviousUtf8Codepoint(const char* in_text_start, const char* in_text_curr);                   // return previous UTF-8 code-point.\n\n// Helpers: File System\n#ifdef IMGUI_DISABLE_FILE_FUNCTIONS\n#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\ntypedef void* ImFileHandle;\nstatic inline ImFileHandle  ImFileOpen(const char*, const char*)                    { return NULL; }\nstatic inline bool          ImFileClose(ImFileHandle)                               { return false; }\nstatic inline ImU64         ImFileGetSize(ImFileHandle)                             { return (ImU64)-1; }\nstatic inline ImU64         ImFileRead(void*, ImU64, ImU64, ImFileHandle)           { return 0; }\nstatic inline ImU64         ImFileWrite(const void*, ImU64, ImU64, ImFileHandle)    { return 0; }\n#endif\n#ifndef IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS\ntypedef FILE* ImFileHandle;\nIMGUI_API ImFileHandle      ImFileOpen(const char* filename, const char* mode);\nIMGUI_API bool              ImFileClose(ImFileHandle file);\nIMGUI_API ImU64             ImFileGetSize(ImFileHandle file);\nIMGUI_API ImU64             ImFileRead(void* data, ImU64 size, ImU64 count, ImFileHandle file);\nIMGUI_API ImU64             ImFileWrite(const void* data, ImU64 size, ImU64 count, ImFileHandle file);\n#else\n#define IMGUI_DISABLE_TTY_FUNCTIONS // Can't use stdout, fflush if we are not using default file functions\n#endif\nIMGUI_API void*             ImFileLoadToMemory(const char* filename, const char* mode, size_t* out_file_size = NULL, int padding_bytes = 0);\n\n// Helpers: Maths\nIM_MSVC_RUNTIME_CHECKS_OFF\n// - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy)\n#ifndef IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS\n#define ImFabs(X)           fabsf(X)\n#define ImSqrt(X)           sqrtf(X)\n#define ImFmod(X, Y)        fmodf((X), (Y))\n#define ImCos(X)            cosf(X)\n#define ImSin(X)            sinf(X)\n#define ImAcos(X)           acosf(X)\n#define ImAtan2(Y, X)       atan2f((Y), (X))\n#define ImAtof(STR)         atof(STR)\n#define ImCeil(X)           ceilf(X)\nstatic inline float  ImPow(float x, float y)    { return powf(x, y); }          // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision\nstatic inline double ImPow(double x, double y)  { return pow(x, y); }\nstatic inline float  ImLog(float x)             { return logf(x); }             // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision\nstatic inline double ImLog(double x)            { return log(x); }\nstatic inline int    ImAbs(int x)               { return x < 0 ? -x : x; }\nstatic inline float  ImAbs(float x)             { return fabsf(x); }\nstatic inline double ImAbs(double x)            { return fabs(x); }\nstatic inline float  ImSign(float x)            { return (x < 0.0f) ? -1.0f : (x > 0.0f) ? 1.0f : 0.0f; } // Sign operator - returns -1, 0 or 1 based on sign of argument\nstatic inline double ImSign(double x)           { return (x < 0.0) ? -1.0 : (x > 0.0) ? 1.0 : 0.0; }\n#ifdef IMGUI_ENABLE_SSE\nstatic inline float  ImRsqrt(float x)           { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); }\n#else\nstatic inline float  ImRsqrt(float x)           { return 1.0f / sqrtf(x); }\n#endif\nstatic inline double ImRsqrt(double x)          { return 1.0 / sqrt(x); }\n#endif\n// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support variety of types: signed/unsigned int/long long float/double\n// (Exceptionally using templates here but we could also redefine them for those types)\ntemplate<typename T> static inline T ImMin(T lhs, T rhs)                        { return lhs < rhs ? lhs : rhs; }\ntemplate<typename T> static inline T ImMax(T lhs, T rhs)                        { return lhs >= rhs ? lhs : rhs; }\ntemplate<typename T> static inline T ImClamp(T v, T mn, T mx)                   { return (v < mn) ? mn : (v > mx) ? mx : v; }\ntemplate<typename T> static inline T ImLerp(T a, T b, float t)                  { return (T)(a + (b - a) * t); }\ntemplate<typename T> static inline void ImSwap(T& a, T& b)                      { T tmp = a; a = b; b = tmp; }\ntemplate<typename T> static inline T ImAddClampOverflow(T a, T b, T mn, T mx)   { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; }\ntemplate<typename T> static inline T ImSubClampOverflow(T a, T b, T mn, T mx)   { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; }\n// - Misc maths helpers\nstatic inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs)                { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); }\nstatic inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs)                { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); }\nstatic inline ImVec2 ImClamp(const ImVec2& v, const ImVec2& mn, ImVec2 mx)      { return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); }\nstatic inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t)          { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); }\nstatic inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t)  { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); }\nstatic inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t)          { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); }\nstatic inline float  ImSaturate(float f)                                        { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; }\nstatic inline float  ImLengthSqr(const ImVec2& lhs)                             { return (lhs.x * lhs.x) + (lhs.y * lhs.y); }\nstatic inline float  ImLengthSqr(const ImVec4& lhs)                             { return (lhs.x * lhs.x) + (lhs.y * lhs.y) + (lhs.z * lhs.z) + (lhs.w * lhs.w); }\nstatic inline float  ImInvLength(const ImVec2& lhs, float fail_value)           { float d = (lhs.x * lhs.x) + (lhs.y * lhs.y); if (d > 0.0f) return ImRsqrt(d); return fail_value; }\nstatic inline float  ImTrunc(float f)                                           { return (float)(int)(f); }\nstatic inline ImVec2 ImTrunc(const ImVec2& v)                                   { return ImVec2((float)(int)(v.x), (float)(int)(v.y)); }\nstatic inline float  ImFloor(float f)                                           { return (float)((f >= 0 || (float)(int)f == f) ? (int)f : (int)f - 1); } // Decent replacement for floorf()\nstatic inline ImVec2 ImFloor(const ImVec2& v)                                   { return ImVec2(ImFloor(v.x), ImFloor(v.y)); }\nstatic inline int    ImModPositive(int a, int b)                                { return (a + b) % b; }\nstatic inline float  ImDot(const ImVec2& a, const ImVec2& b)                    { return a.x * b.x + a.y * b.y; }\nstatic inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a)        { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); }\nstatic inline float  ImLinearSweep(float current, float target, float speed)    { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; }\nstatic inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs)                { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); }\nstatic inline bool   ImIsFloatAboveGuaranteedIntegerPrecision(float f)          { return f <= -16777216 || f >= 16777216; }\nstatic inline float  ImExponentialMovingAverage(float avg, float sample, int n) { avg -= avg / n; avg += sample / n; return avg; }\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n// Helpers: Geometry\nIMGUI_API ImVec2     ImBezierCubicCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, float t);\nIMGUI_API ImVec2     ImBezierCubicClosestPoint(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, int num_segments);       // For curves with explicit number of segments\nIMGUI_API ImVec2     ImBezierCubicClosestPointCasteljau(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, const ImVec2& p, float tess_tol);// For auto-tessellated curves you can use tess_tol = style.CurveTessellationTol\nIMGUI_API ImVec2     ImBezierQuadraticCalc(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float t);\nIMGUI_API ImVec2     ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p);\nIMGUI_API bool       ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p);\nIMGUI_API ImVec2     ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p);\nIMGUI_API void       ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w);\ninline float         ImTriangleArea(const ImVec2& a, const ImVec2& b, const ImVec2& c) { return ImFabs((a.x * (b.y - c.y)) + (b.x * (c.y - a.y)) + (c.x * (a.y - b.y))) * 0.5f; }\n\n// Helper: ImVec1 (1D vector)\n// (this odd construct is used to facilitate the transition between 1D and 2D, and the maintenance of some branches/patches)\nIM_MSVC_RUNTIME_CHECKS_OFF\nstruct ImVec1\n{\n    float   x;\n    constexpr ImVec1()         : x(0.0f) { }\n    constexpr ImVec1(float _x) : x(_x) { }\n};\n\n// Helper: ImVec2ih (2D vector, half-size integer, for long-term packed storage)\nstruct ImVec2ih\n{\n    short   x, y;\n    constexpr ImVec2ih()                           : x(0), y(0) {}\n    constexpr ImVec2ih(short _x, short _y)         : x(_x), y(_y) {}\n    constexpr explicit ImVec2ih(const ImVec2& rhs) : x((short)rhs.x), y((short)rhs.y) {}\n};\n\n// Helper: ImRect (2D axis aligned bounding-box)\n// NB: we can't rely on ImVec2 math operators being available here!\nstruct IMGUI_API ImRect\n{\n    ImVec2      Min;    // Upper-left\n    ImVec2      Max;    // Lower-right\n\n    constexpr ImRect()                                        : Min(0.0f, 0.0f), Max(0.0f, 0.0f)  {}\n    constexpr ImRect(const ImVec2& min, const ImVec2& max)    : Min(min), Max(max)                {}\n    constexpr ImRect(const ImVec4& v)                         : Min(v.x, v.y), Max(v.z, v.w)      {}\n    constexpr ImRect(float x1, float y1, float x2, float y2)  : Min(x1, y1), Max(x2, y2)          {}\n\n    ImVec2      GetCenter() const                   { return ImVec2((Min.x + Max.x) * 0.5f, (Min.y + Max.y) * 0.5f); }\n    ImVec2      GetSize() const                     { return ImVec2(Max.x - Min.x, Max.y - Min.y); }\n    float       GetWidth() const                    { return Max.x - Min.x; }\n    float       GetHeight() const                   { return Max.y - Min.y; }\n    float       GetArea() const                     { return (Max.x - Min.x) * (Max.y - Min.y); }\n    ImVec2      GetTL() const                       { return Min; }                   // Top-left\n    ImVec2      GetTR() const                       { return ImVec2(Max.x, Min.y); }  // Top-right\n    ImVec2      GetBL() const                       { return ImVec2(Min.x, Max.y); }  // Bottom-left\n    ImVec2      GetBR() const                       { return Max; }                   // Bottom-right\n    bool        Contains(const ImVec2& p) const     { return p.x     >= Min.x && p.y     >= Min.y && p.x     <  Max.x && p.y     <  Max.y; }\n    bool        Contains(const ImRect& r) const     { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; }\n    bool        ContainsWithPad(const ImVec2& p, const ImVec2& pad) const { return p.x >= Min.x - pad.x && p.y >= Min.y - pad.y && p.x < Max.x + pad.x && p.y < Max.y + pad.y; }\n    bool        Overlaps(const ImRect& r) const     { return r.Min.y <  Max.y && r.Max.y >  Min.y && r.Min.x <  Max.x && r.Max.x >  Min.x; }\n    void        Add(const ImVec2& p)                { if (Min.x > p.x)     Min.x = p.x;     if (Min.y > p.y)     Min.y = p.y;     if (Max.x < p.x)     Max.x = p.x;     if (Max.y < p.y)     Max.y = p.y; }\n    void        Add(const ImRect& r)                { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; }\n    void        Expand(const float amount)          { Min.x -= amount;   Min.y -= amount;   Max.x += amount;   Max.y += amount; }\n    void        Expand(const ImVec2& amount)        { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; }\n    void        Translate(const ImVec2& d)          { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; }\n    void        TranslateX(float dx)                { Min.x += dx; Max.x += dx; }\n    void        TranslateY(float dy)                { Min.y += dy; Max.y += dy; }\n    void        ClipWith(const ImRect& r)           { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); }                   // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display.\n    void        ClipWithFull(const ImRect& r)       { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped.\n    void        Floor()                             { Min.x = IM_TRUNC(Min.x); Min.y = IM_TRUNC(Min.y); Max.x = IM_TRUNC(Max.x); Max.y = IM_TRUNC(Max.y); }\n    bool        IsInverted() const                  { return Min.x > Max.x || Min.y > Max.y; }\n    ImVec4      ToVec4() const                      { return ImVec4(Min.x, Min.y, Max.x, Max.y); }\n};\n\n// Helper: ImBitArray\n#define         IM_BITARRAY_TESTBIT(_ARRAY, _N)                 ((_ARRAY[(_N) >> 5] & ((ImU32)1 << ((_N) & 31))) != 0) // Macro version of ImBitArrayTestBit(): ensure args have side-effect or are costly!\n#define         IM_BITARRAY_CLEARBIT(_ARRAY, _N)                ((_ARRAY[(_N) >> 5] &= ~((ImU32)1 << ((_N) & 31))))    // Macro version of ImBitArrayClearBit(): ensure args have side-effect or are costly!\ninline size_t   ImBitArrayGetStorageSizeInBytes(int bitcount)   { return (size_t)((bitcount + 31) >> 5) << 2; }\ninline void     ImBitArrayClearAllBits(ImU32* arr, int bitcount){ memset(arr, 0, ImBitArrayGetStorageSizeInBytes(bitcount)); }\ninline bool     ImBitArrayTestBit(const ImU32* arr, int n)      { ImU32 mask = (ImU32)1 << (n & 31); return (arr[n >> 5] & mask) != 0; }\ninline void     ImBitArrayClearBit(ImU32* arr, int n)           { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] &= ~mask; }\ninline void     ImBitArraySetBit(ImU32* arr, int n)             { ImU32 mask = (ImU32)1 << (n & 31); arr[n >> 5] |= mask; }\ninline void     ImBitArraySetBitRange(ImU32* arr, int n, int n2) // Works on range [n..n2)\n{\n    n2--;\n    while (n <= n2)\n    {\n        int a_mod = (n & 31);\n        int b_mod = (n2 > (n | 31) ? 31 : (n2 & 31)) + 1;\n        ImU32 mask = (ImU32)(((ImU64)1 << b_mod) - 1) & ~(ImU32)(((ImU64)1 << a_mod) - 1);\n        arr[n >> 5] |= mask;\n        n = (n + 32) & ~31;\n    }\n}\n\ntypedef ImU32* ImBitArrayPtr; // Name for use in structs\n\n// Helper: ImBitArray class (wrapper over ImBitArray functions)\n// Store 1-bit per value.\ntemplate<int BITCOUNT, int OFFSET = 0>\nstruct ImBitArray\n{\n    ImU32           Storage[(BITCOUNT + 31) >> 5];\n    ImBitArray()                                { ClearAllBits(); }\n    void            ClearAllBits()              { memset(Storage, 0, sizeof(Storage)); }\n    void            SetAllBits()                { memset(Storage, 255, sizeof(Storage)); }\n    bool            TestBit(int n) const        { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); }\n    void            SetBit(int n)               { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArraySetBit(Storage, n); }\n    void            ClearBit(int n)             { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); ImBitArrayClearBit(Storage, n); }\n    void            SetBitRange(int n, int n2)  { n += OFFSET; n2 += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT && n2 > n && n2 <= BITCOUNT); ImBitArraySetBitRange(Storage, n, n2); } // Works on range [n..n2)\n    bool            operator[](int n) const     { n += OFFSET; IM_ASSERT(n >= 0 && n < BITCOUNT); return IM_BITARRAY_TESTBIT(Storage, n); }\n};\n\n// Helper: ImBitVector\n// Store 1-bit per value.\nstruct IMGUI_API ImBitVector\n{\n    ImVector<ImU32> Storage;\n    void            Create(int sz)              { Storage.resize((sz + 31) >> 5); memset(Storage.Data, 0, (size_t)Storage.Size * sizeof(Storage.Data[0])); }\n    void            Clear()                     { Storage.clear(); }\n    bool            TestBit(int n) const        { IM_ASSERT(n < (Storage.Size << 5)); return IM_BITARRAY_TESTBIT(Storage.Data, n); }\n    void            SetBit(int n)               { IM_ASSERT(n < (Storage.Size << 5)); ImBitArraySetBit(Storage.Data, n); }\n    void            ClearBit(int n)             { IM_ASSERT(n < (Storage.Size << 5)); ImBitArrayClearBit(Storage.Data, n); }\n};\nIM_MSVC_RUNTIME_CHECKS_RESTORE\n\n// Helper: ImSpan<>\n// Pointing to a span of data we don't own.\ntemplate<typename T>\nstruct ImSpan\n{\n    T*                  Data;\n    T*                  DataEnd;\n\n    // Constructors, destructor\n    inline ImSpan()                                 { Data = DataEnd = NULL; }\n    inline ImSpan(T* data, int size)                { Data = data; DataEnd = data + size; }\n    inline ImSpan(T* data, T* data_end)             { Data = data; DataEnd = data_end; }\n\n    inline void         set(T* data, int size)      { Data = data; DataEnd = data + size; }\n    inline void         set(T* data, T* data_end)   { Data = data; DataEnd = data_end; }\n    inline int          size() const                { return (int)(ptrdiff_t)(DataEnd - Data); }\n    inline int          size_in_bytes() const       { return (int)(ptrdiff_t)(DataEnd - Data) * (int)sizeof(T); }\n    inline T&           operator[](int i)           { T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; }\n    inline const T&     operator[](int i) const     { const T* p = Data + i; IM_ASSERT(p >= Data && p < DataEnd); return *p; }\n\n    inline T*           begin()                     { return Data; }\n    inline const T*     begin() const               { return Data; }\n    inline T*           end()                       { return DataEnd; }\n    inline const T*     end() const                 { return DataEnd; }\n\n    // Utilities\n    inline int  index_from_ptr(const T* it) const   { IM_ASSERT(it >= Data && it < DataEnd); const ptrdiff_t off = it - Data; return (int)off; }\n};\n\n// Helper: ImSpanAllocator<>\n// Facilitate storing multiple chunks into a single large block (the \"arena\")\n// - Usage: call Reserve() N times, allocate GetArenaSizeInBytes() worth, pass it to SetArenaBasePtr(), call GetSpan() N times to retrieve the aligned ranges.\ntemplate<int CHUNKS>\nstruct ImSpanAllocator\n{\n    char*   BasePtr;\n    int     CurrOff;\n    int     CurrIdx;\n    int     Offsets[CHUNKS];\n    int     Sizes[CHUNKS];\n\n    ImSpanAllocator()                               { memset(this, 0, sizeof(*this)); }\n    inline void  Reserve(int n, size_t sz, int a=4) { IM_ASSERT(n == CurrIdx && n < CHUNKS); CurrOff = IM_MEMALIGN(CurrOff, a); Offsets[n] = CurrOff; Sizes[n] = (int)sz; CurrIdx++; CurrOff += (int)sz; }\n    inline int   GetArenaSizeInBytes()              { return CurrOff; }\n    inline void  SetArenaBasePtr(void* base_ptr)    { BasePtr = (char*)base_ptr; }\n    inline void* GetSpanPtrBegin(int n)             { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n]); }\n    inline void* GetSpanPtrEnd(int n)               { IM_ASSERT(n >= 0 && n < CHUNKS && CurrIdx == CHUNKS); return (void*)(BasePtr + Offsets[n] + Sizes[n]); }\n    template<typename T>\n    inline void  GetSpan(int n, ImSpan<T>* span)    { span->set((T*)GetSpanPtrBegin(n), (T*)GetSpanPtrEnd(n)); }\n};\n\n// Helper: ImPool<>\n// Basic keyed storage for contiguous instances, slow/amortized insertion, O(1) indexable, O(Log N) queries by ID over a dense/hot buffer,\n// Honor constructor/destructor. Add/remove invalidate all pointers. Indexes have the same lifetime as the associated object.\ntypedef int ImPoolIdx;\ntemplate<typename T>\nstruct ImPool\n{\n    ImVector<T>     Buf;        // Contiguous data\n    ImGuiStorage    Map;        // ID->Index\n    ImPoolIdx       FreeIdx;    // Next free idx to use\n    ImPoolIdx       AliveCount; // Number of active/alive items (for display purpose)\n\n    ImPool()    { FreeIdx = AliveCount = 0; }\n    ~ImPool()   { Clear(); }\n    T*          GetByKey(ImGuiID key)               { int idx = Map.GetInt(key, -1); return (idx != -1) ? &Buf[idx] : NULL; }\n    T*          GetByIndex(ImPoolIdx n)             { return &Buf[n]; }\n    ImPoolIdx   GetIndex(const T* p) const          { IM_ASSERT(p >= Buf.Data && p < Buf.Data + Buf.Size); return (ImPoolIdx)(p - Buf.Data); }\n    T*          GetOrAddByKey(ImGuiID key)          { int* p_idx = Map.GetIntRef(key, -1); if (*p_idx != -1) return &Buf[*p_idx]; *p_idx = FreeIdx; return Add(); }\n    bool        Contains(const T* p) const          { return (p >= Buf.Data && p < Buf.Data + Buf.Size); }\n    void        Clear()                             { for (int n = 0; n < Map.Data.Size; n++) { int idx = Map.Data[n].val_i; if (idx != -1) Buf[idx].~T(); } Map.Clear(); Buf.clear(); FreeIdx = AliveCount = 0; }\n    T*          Add()                               { int idx = FreeIdx; if (idx == Buf.Size) { Buf.resize(Buf.Size + 1); FreeIdx++; } else { FreeIdx = *(int*)&Buf[idx]; } IM_PLACEMENT_NEW(&Buf[idx]) T(); AliveCount++; return &Buf[idx]; }\n    void        Remove(ImGuiID key, const T* p)     { Remove(key, GetIndex(p)); }\n    void        Remove(ImGuiID key, ImPoolIdx idx)  { Buf[idx].~T(); *(int*)&Buf[idx] = FreeIdx; FreeIdx = idx; Map.SetInt(key, -1); AliveCount--; }\n    void        Reserve(int capacity)               { Buf.reserve(capacity); Map.Data.reserve(capacity); }\n\n    // To iterate a ImPool: for (int n = 0; n < pool.GetMapSize(); n++) if (T* t = pool.TryGetMapData(n)) { ... }\n    // Can be avoided if you know .Remove() has never been called on the pool, or AliveCount == GetMapSize()\n    int         GetAliveCount() const               { return AliveCount; }      // Number of active/alive items in the pool (for display purpose)\n    int         GetBufSize() const                  { return Buf.Size; }\n    int         GetMapSize() const                  { return Map.Data.Size; }   // It is the map we need iterate to find valid items, since we don't have \"alive\" storage anywhere\n    T*          TryGetMapData(ImPoolIdx n)          { int idx = Map.Data[n].val_i; if (idx == -1) return NULL; return GetByIndex(idx); }\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    int         GetSize()                           { return GetMapSize(); } // For ImPlot: should use GetMapSize() from (IMGUI_VERSION_NUM >= 18304)\n#endif\n};\n\n// Helper: ImChunkStream<>\n// Build and iterate a contiguous stream of variable-sized structures.\n// This is used by Settings to store persistent data while reducing allocation count.\n// We store the chunk size first, and align the final size on 4 bytes boundaries.\n// The tedious/zealous amount of casting is to avoid -Wcast-align warnings.\ntemplate<typename T>\nstruct ImChunkStream\n{\n    ImVector<char>  Buf;\n\n    void    clear()                     { Buf.clear(); }\n    bool    empty() const               { return Buf.Size == 0; }\n    int     size() const                { return Buf.Size; }\n    T*      alloc_chunk(size_t sz)      { size_t HDR_SZ = 4; sz = IM_MEMALIGN(HDR_SZ + sz, 4u); int off = Buf.Size; Buf.resize(off + (int)sz); ((int*)(void*)(Buf.Data + off))[0] = (int)sz; return (T*)(void*)(Buf.Data + off + (int)HDR_SZ); }\n    T*      begin()                     { size_t HDR_SZ = 4; if (!Buf.Data) return NULL; return (T*)(void*)(Buf.Data + HDR_SZ); }\n    T*      next_chunk(T* p)            { size_t HDR_SZ = 4; IM_ASSERT(p >= begin() && p < end()); p = (T*)(void*)((char*)(void*)p + chunk_size(p)); if (p == (T*)(void*)((char*)end() + HDR_SZ)) return (T*)0; IM_ASSERT(p < end()); return p; }\n    int     chunk_size(const T* p)      { return ((const int*)p)[-1]; }\n    T*      end()                       { return (T*)(void*)(Buf.Data + Buf.Size); }\n    int     offset_from_ptr(const T* p) { IM_ASSERT(p >= begin() && p < end()); const ptrdiff_t off = (const char*)p - Buf.Data; return (int)off; }\n    T*      ptr_from_offset(int off)    { IM_ASSERT(off >= 4 && off < Buf.Size); return (T*)(void*)(Buf.Data + off); }\n    void    swap(ImChunkStream<T>& rhs) { rhs.Buf.swap(Buf); }\n};\n\n// Helper: ImGuiTextIndex<>\n// Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API.\nstruct ImGuiTextIndex\n{\n    ImVector<int>   LineOffsets;\n    int             EndOffset = 0;                          // Because we don't own text buffer we need to maintain EndOffset (may bake in LineOffsets?)\n\n    void            clear()                                 { LineOffsets.clear(); EndOffset = 0; }\n    int             size()                                  { return LineOffsets.Size; }\n    const char*     get_line_begin(const char* base, int n) { return base + LineOffsets[n]; }\n    const char*     get_line_end(const char* base, int n)   { return base + (n + 1 < LineOffsets.Size ? (LineOffsets[n + 1] - 1) : EndOffset); }\n    void            append(const char* base, int old_size, int new_size);\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImDrawList support\n//-----------------------------------------------------------------------------\n\n// ImDrawList: Helper function to calculate a circle's segment count given its radius and a \"maximum error\" value.\n// Estimation of number of circle segment based on error is derived using method described in https://stackoverflow.com/a/2244088/15194693\n// Number of segments (N) is calculated using equation:\n//   N = ceil ( pi / acos(1 - error / r) )     where r > 0, error <= r\n// Our equation is significantly simpler that one in the post thanks for choosing segment that is\n// perpendicular to X axis. Follow steps in the article from this starting condition and you will\n// will get this result.\n//\n// Rendering circles with an odd number of segments, while mathematically correct will produce\n// asymmetrical results on the raster grid. Therefore we're rounding N to next even number (7->8, 8->8, 9->10 etc.)\n#define IM_ROUNDUP_TO_EVEN(_V)                                  ((((_V) + 1) / 2) * 2)\n#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN                     4\n#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX                     512\n#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC(_RAD,_MAXERROR)    ImClamp(IM_ROUNDUP_TO_EVEN((int)ImCeil(IM_PI / ImAcos(1 - ImMin((_MAXERROR), (_RAD)) / (_RAD)))), IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MIN, IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_MAX)\n\n// Raw equation from IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC rewritten for 'r' and 'error'.\n#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_R(_N,_MAXERROR)    ((_MAXERROR) / (1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))))\n#define IM_DRAWLIST_CIRCLE_AUTO_SEGMENT_CALC_ERROR(_N,_RAD)     ((1 - ImCos(IM_PI / ImMax((float)(_N), IM_PI))) / (_RAD))\n\n// ImDrawList: Lookup table size for adaptive arc drawing, cover full circle.\n#ifndef IM_DRAWLIST_ARCFAST_TABLE_SIZE\n#define IM_DRAWLIST_ARCFAST_TABLE_SIZE                          48 // Number of samples in lookup table.\n#endif\n#define IM_DRAWLIST_ARCFAST_SAMPLE_MAX                          IM_DRAWLIST_ARCFAST_TABLE_SIZE // Sample index _PathArcToFastEx() for 360 angle.\n\n// Data shared between all ImDrawList instances\n// You may want to create your own instance of this if you want to use ImDrawList completely without ImGui. In that case, watch out for future changes to this structure.\nstruct IMGUI_API ImDrawListSharedData\n{\n    ImVec2          TexUvWhitePixel;            // UV of white pixel in the atlas\n    ImFont*         Font;                       // Current/default font (optional, for simplified AddText overload)\n    float           FontSize;                   // Current/default font size (optional, for simplified AddText overload)\n    float           CurveTessellationTol;       // Tessellation tolerance when using PathBezierCurveTo()\n    float           CircleSegmentMaxError;      // Number of circle segments to use per pixel of radius for AddCircle() etc\n    ImVec4          ClipRectFullscreen;         // Value for PushClipRectFullscreen()\n    ImDrawListFlags InitialFlags;               // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards)\n\n    // [Internal] Temp write buffer\n    ImVector<ImVec2> TempBuffer;\n\n    // [Internal] Lookup tables\n    ImVec2          ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle.\n    float           ArcFastRadiusCutoff;                        // Cutoff radius after which arc drawing will fallback to slower PathArcTo()\n    ImU8            CircleSegmentCounts[64];    // Precomputed segment count for given radius before we calculate it dynamically (to avoid calculation overhead)\n    const ImVec4*   TexUvLines;                 // UV of anti-aliased lines in the atlas\n\n    ImDrawListSharedData();\n    void SetCircleTessellationMaxError(float max_error);\n};\n\nstruct ImDrawDataBuilder\n{\n    ImVector<ImDrawList*>*  Layers[2];      // Pointers to global layers for: regular, tooltip. LayersP[0] is owned by DrawData.\n    ImVector<ImDrawList*>   LayerData1;\n\n    ImDrawDataBuilder()                     { memset(this, 0, sizeof(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Widgets support: flags, enums, data structures\n//-----------------------------------------------------------------------------\n\n// Flags used by upcoming items\n// - input: PushItemFlag() manipulates g.CurrentItemFlags, ItemAdd() calls may add extra flags.\n// - output: stored in g.LastItemData.InFlags\n// Current window shared by all windows.\n// This is going to be exposed in imgui.h when stabilized enough.\nenum ImGuiItemFlags_\n{\n    // Controlled by user\n    ImGuiItemFlags_None                     = 0,\n    ImGuiItemFlags_NoTabStop                = 1 << 0,  // false     // Disable keyboard tabbing. This is a \"lighter\" version of ImGuiItemFlags_NoNav.\n    ImGuiItemFlags_ButtonRepeat             = 1 << 1,  // false     // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings.\n    ImGuiItemFlags_Disabled                 = 1 << 2,  // false     // Disable interactions but doesn't affect visuals. See BeginDisabled()/EndDisabled(). See github.com/ocornut/imgui/issues/211\n    ImGuiItemFlags_NoNav                    = 1 << 3,  // false     // Disable any form of focusing (keyboard/gamepad directional navigation and SetKeyboardFocusHere() calls)\n    ImGuiItemFlags_NoNavDefaultFocus        = 1 << 4,  // false     // Disable item being a candidate for default focus (e.g. used by title bar items)\n    ImGuiItemFlags_SelectableDontClosePopup = 1 << 5,  // false     // Disable MenuItem/Selectable() automatically closing their popup window\n    ImGuiItemFlags_MixedValue               = 1 << 6,  // false     // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets)\n    ImGuiItemFlags_ReadOnly                 = 1 << 7,  // false     // [ALPHA] Allow hovering interactions but underlying value is not changed.\n    ImGuiItemFlags_NoWindowHoverableCheck   = 1 << 8,  // false     // Disable hoverable check in ItemHoverable()\n    ImGuiItemFlags_AllowOverlap             = 1 << 9,  // false     // Allow being overlapped by another widget. Not-hovered to Hovered transition deferred by a frame.\n\n    // Controlled by widget code\n    ImGuiItemFlags_Inputable                = 1 << 10, // false     // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature.\n    ImGuiItemFlags_HasSelectionUserData     = 1 << 11, // false     // Set by SetNextItemSelectionUserData()\n};\n\n// Status flags for an already submitted item\n// - output: stored in g.LastItemData.StatusFlags\nenum ImGuiItemStatusFlags_\n{\n    ImGuiItemStatusFlags_None               = 0,\n    ImGuiItemStatusFlags_HoveredRect        = 1 << 0,   // Mouse position is within item rectangle (does NOT mean that the window is in correct z-order and can be hovered!, this is only one part of the most-common IsItemHovered test)\n    ImGuiItemStatusFlags_HasDisplayRect     = 1 << 1,   // g.LastItemData.DisplayRect is valid\n    ImGuiItemStatusFlags_Edited             = 1 << 2,   // Value exposed by item was edited in the current frame (should match the bool return value of most widgets)\n    ImGuiItemStatusFlags_ToggledSelection   = 1 << 3,   // Set when Selectable(), TreeNode() reports toggling a selection. We can't report \"Selected\", only state changes, in order to easily handle clipping with less issues.\n    ImGuiItemStatusFlags_ToggledOpen        = 1 << 4,   // Set when TreeNode() reports toggling their open state.\n    ImGuiItemStatusFlags_HasDeactivated     = 1 << 5,   // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag.\n    ImGuiItemStatusFlags_Deactivated        = 1 << 6,   // Only valid if ImGuiItemStatusFlags_HasDeactivated is set.\n    ImGuiItemStatusFlags_HoveredWindow      = 1 << 7,   // Override the HoveredWindow test to allow cross-window hover testing.\n    ImGuiItemStatusFlags_FocusedByTabbing   = 1 << 8,   // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon)\n    ImGuiItemStatusFlags_Visible            = 1 << 9,   // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()).\n\n    // Additional status + semantic for ImGuiTestEngine\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    ImGuiItemStatusFlags_Openable           = 1 << 20,  // Item is an openable (e.g. TreeNode)\n    ImGuiItemStatusFlags_Opened             = 1 << 21,  // Opened status\n    ImGuiItemStatusFlags_Checkable          = 1 << 22,  // Item is a checkable (e.g. CheckBox, MenuItem)\n    ImGuiItemStatusFlags_Checked            = 1 << 23,  // Checked status\n    ImGuiItemStatusFlags_Inputable          = 1 << 24,  // Item is a text-inputable (e.g. InputText, SliderXXX, DragXXX)\n#endif\n};\n\n// Extend ImGuiHoveredFlags_\nenum ImGuiHoveredFlagsPrivate_\n{\n    ImGuiHoveredFlags_DelayMask_                    = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay,\n    ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary,\n    ImGuiHoveredFlags_AllowedMaskForIsItemHovered   = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_,\n};\n\n// Extend ImGuiInputTextFlags_\nenum ImGuiInputTextFlagsPrivate_\n{\n    // [Internal]\n    ImGuiInputTextFlags_Multiline           = 1 << 26,  // For internal use by InputTextMultiline()\n    ImGuiInputTextFlags_NoMarkEdited        = 1 << 27,  // For internal use by functions using InputText() before reformatting data\n    ImGuiInputTextFlags_MergedItem          = 1 << 28,  // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match.\n};\n\n// Extend ImGuiButtonFlags_\nenum ImGuiButtonFlagsPrivate_\n{\n    ImGuiButtonFlags_PressedOnClick         = 1 << 4,   // return true on click (mouse down event)\n    ImGuiButtonFlags_PressedOnClickRelease  = 1 << 5,   // [Default] return true on click + release on same item <-- this is what the majority of Button are using\n    ImGuiButtonFlags_PressedOnClickReleaseAnywhere = 1 << 6, // return true on click + release even if the release event is not done while hovering the item\n    ImGuiButtonFlags_PressedOnRelease       = 1 << 7,   // return true on release (default requires click+release)\n    ImGuiButtonFlags_PressedOnDoubleClick   = 1 << 8,   // return true on double-click (default requires click+release)\n    ImGuiButtonFlags_PressedOnDragDropHold  = 1 << 9,   // return true when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers)\n    ImGuiButtonFlags_Repeat                 = 1 << 10,  // hold to repeat\n    ImGuiButtonFlags_FlattenChildren        = 1 << 11,  // allow interactions even if a child window is overlapping\n    ImGuiButtonFlags_AllowOverlap           = 1 << 12,  // require previous frame HoveredId to either match id or be null before being usable.\n    ImGuiButtonFlags_DontClosePopups        = 1 << 13,  // disable automatically closing parent popup on press // [UNUSED]\n    //ImGuiButtonFlags_Disabled             = 1 << 14,  // disable interactions -> use BeginDisabled() or ImGuiItemFlags_Disabled\n    ImGuiButtonFlags_AlignTextBaseLine      = 1 << 15,  // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine\n    ImGuiButtonFlags_NoKeyModifiers         = 1 << 16,  // disable mouse interaction if a key modifier is held\n    ImGuiButtonFlags_NoHoldingActiveId      = 1 << 17,  // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only)\n    ImGuiButtonFlags_NoNavFocus             = 1 << 18,  // don't override navigation focus when activated (FIXME: this is essentially used everytime an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.InFlags)\n    ImGuiButtonFlags_NoHoveredOnFocus       = 1 << 19,  // don't report as hovered when nav focus is on this item\n    ImGuiButtonFlags_NoSetKeyOwner          = 1 << 20,  // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)\n    ImGuiButtonFlags_NoTestKeyOwner         = 1 << 21,  // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)\n    ImGuiButtonFlags_PressedOnMask_         = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold,\n    ImGuiButtonFlags_PressedOnDefault_      = ImGuiButtonFlags_PressedOnClickRelease,\n};\n\n// Extend ImGuiComboFlags_\nenum ImGuiComboFlagsPrivate_\n{\n    ImGuiComboFlags_CustomPreview           = 1 << 20,  // enable BeginComboPreview()\n};\n\n// Extend ImGuiSliderFlags_\nenum ImGuiSliderFlagsPrivate_\n{\n    ImGuiSliderFlags_Vertical               = 1 << 20,  // Should this slider be orientated vertically?\n    ImGuiSliderFlags_ReadOnly               = 1 << 21,  // Consider using g.NextItemData.ItemFlags |= ImGuiItemFlags_ReadOnly instead.\n};\n\n// Extend ImGuiSelectableFlags_\nenum ImGuiSelectableFlagsPrivate_\n{\n    // NB: need to be in sync with last value of ImGuiSelectableFlags_\n    ImGuiSelectableFlags_NoHoldingActiveID      = 1 << 20,\n    ImGuiSelectableFlags_SelectOnNav            = 1 << 21,  // (WIP) Auto-select when moved into. This is not exposed in public API as to handle multi-select and modifiers we will need user to explicitly control focus scope. May be replaced with a BeginSelection() API.\n    ImGuiSelectableFlags_SelectOnClick          = 1 << 22,  // Override button behavior to react on Click (default is Click+Release)\n    ImGuiSelectableFlags_SelectOnRelease        = 1 << 23,  // Override button behavior to react on Release (default is Click+Release)\n    ImGuiSelectableFlags_SpanAvailWidth         = 1 << 24,  // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus)\n    ImGuiSelectableFlags_SetNavIdOnHover        = 1 << 25,  // Set Nav/Focus ID on mouse hover (used by MenuItem)\n    ImGuiSelectableFlags_NoPadWithHalfSpacing   = 1 << 26,  // Disable padding each side with ItemSpacing * 0.5f\n    ImGuiSelectableFlags_NoSetKeyOwner          = 1 << 27,  // Don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!)\n};\n\n// Extend ImGuiTreeNodeFlags_\nenum ImGuiTreeNodeFlagsPrivate_\n{\n    ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20,\n    ImGuiTreeNodeFlags_UpsideDownArrow            = 1 << 21,// (FIXME-WIP) Turn Down arrow into an Up arrow, but reversed trees (#6517)\n};\n\nenum ImGuiSeparatorFlags_\n{\n    ImGuiSeparatorFlags_None                    = 0,\n    ImGuiSeparatorFlags_Horizontal              = 1 << 0,   // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar\n    ImGuiSeparatorFlags_Vertical                = 1 << 1,\n    ImGuiSeparatorFlags_SpanAllColumns          = 1 << 2,   // Make separator cover all columns of a legacy Columns() set.\n};\n\n// Flags for FocusWindow(). This is not called ImGuiFocusFlags to avoid confusion with public-facing ImGuiFocusedFlags.\n// FIXME: Once we finishing replacing more uses of GetTopMostPopupModal()+IsWindowWithinBeginStackOf()\n// and FindBlockingModal() with this, we may want to change the flag to be opt-out instead of opt-in.\nenum ImGuiFocusRequestFlags_\n{\n    ImGuiFocusRequestFlags_None                 = 0,\n    ImGuiFocusRequestFlags_RestoreFocusedChild  = 1 << 0,   // Find last focused child (if any) and focus it instead.\n    ImGuiFocusRequestFlags_UnlessBelowModal     = 1 << 1,   // Do not set focus if the window is below a modal.\n};\n\nenum ImGuiTextFlags_\n{\n    ImGuiTextFlags_None                         = 0,\n    ImGuiTextFlags_NoWidthForLargeClippedText   = 1 << 0,\n};\n\nenum ImGuiTooltipFlags_\n{\n    ImGuiTooltipFlags_None                      = 0,\n    ImGuiTooltipFlags_OverridePrevious          = 1 << 1,   // Clear/ignore previously submitted tooltip (defaults to append)\n};\n\n// FIXME: this is in development, not exposed/functional as a generic feature yet.\n// Horizontal/Vertical enums are fixed to 0/1 so they may be used to index ImVec2\nenum ImGuiLayoutType_\n{\n    ImGuiLayoutType_Horizontal = 0,\n    ImGuiLayoutType_Vertical = 1\n};\n\nenum ImGuiLogType\n{\n    ImGuiLogType_None = 0,\n    ImGuiLogType_TTY,\n    ImGuiLogType_File,\n    ImGuiLogType_Buffer,\n    ImGuiLogType_Clipboard,\n};\n\n// X/Y enums are fixed to 0/1 so they may be used to index ImVec2\nenum ImGuiAxis\n{\n    ImGuiAxis_None = -1,\n    ImGuiAxis_X = 0,\n    ImGuiAxis_Y = 1\n};\n\nenum ImGuiPlotType\n{\n    ImGuiPlotType_Lines,\n    ImGuiPlotType_Histogram,\n};\n\nenum ImGuiPopupPositionPolicy\n{\n    ImGuiPopupPositionPolicy_Default,\n    ImGuiPopupPositionPolicy_ComboBox,\n    ImGuiPopupPositionPolicy_Tooltip,\n};\n\nstruct ImGuiDataVarInfo\n{\n    ImGuiDataType   Type;\n    ImU32           Count;      // 1+\n    ImU32           Offset;     // Offset in parent structure\n    void* GetVarPtr(void* parent) const { return (void*)((unsigned char*)parent + Offset); }\n};\n\nstruct ImGuiDataTypeTempStorage\n{\n    ImU8        Data[8];        // Can fit any data up to ImGuiDataType_COUNT\n};\n\n// Type information associated to one ImGuiDataType. Retrieve with DataTypeGetInfo().\nstruct ImGuiDataTypeInfo\n{\n    size_t      Size;           // Size in bytes\n    const char* Name;           // Short descriptive name for the type, for debugging\n    const char* PrintFmt;       // Default printf format for the type\n    const char* ScanFmt;        // Default scanf format for the type\n};\n\n// Extend ImGuiDataType_\nenum ImGuiDataTypePrivate_\n{\n    ImGuiDataType_String = ImGuiDataType_COUNT + 1,\n    ImGuiDataType_Pointer,\n    ImGuiDataType_ID,\n};\n\n// Stacked color modifier, backup of modified data so we can restore it\nstruct ImGuiColorMod\n{\n    ImGuiCol        Col;\n    ImVec4          BackupValue;\n};\n\n// Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable.\nstruct ImGuiStyleMod\n{\n    ImGuiStyleVar   VarIdx;\n    union           { int BackupInt[2]; float BackupFloat[2]; };\n    ImGuiStyleMod(ImGuiStyleVar idx, int v)     { VarIdx = idx; BackupInt[0] = v; }\n    ImGuiStyleMod(ImGuiStyleVar idx, float v)   { VarIdx = idx; BackupFloat[0] = v; }\n    ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v)  { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; }\n};\n\n// Storage data for BeginComboPreview()/EndComboPreview()\nstruct IMGUI_API ImGuiComboPreviewData\n{\n    ImRect          PreviewRect;\n    ImVec2          BackupCursorPos;\n    ImVec2          BackupCursorMaxPos;\n    ImVec2          BackupCursorPosPrevLine;\n    float           BackupPrevLineTextBaseOffset;\n    ImGuiLayoutType BackupLayout;\n\n    ImGuiComboPreviewData() { memset(this, 0, sizeof(*this)); }\n};\n\n// Stacked storage data for BeginGroup()/EndGroup()\nstruct IMGUI_API ImGuiGroupData\n{\n    ImGuiID     WindowID;\n    ImVec2      BackupCursorPos;\n    ImVec2      BackupCursorMaxPos;\n    ImVec2      BackupCursorPosPrevLine;\n    ImVec1      BackupIndent;\n    ImVec1      BackupGroupOffset;\n    ImVec2      BackupCurrLineSize;\n    float       BackupCurrLineTextBaseOffset;\n    ImGuiID     BackupActiveIdIsAlive;\n    bool        BackupActiveIdPreviousFrameIsAlive;\n    bool        BackupHoveredIdIsAlive;\n    bool        BackupIsSameLine;\n    bool        EmitItem;\n};\n\n// Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper.\nstruct IMGUI_API ImGuiMenuColumns\n{\n    ImU32       TotalWidth;\n    ImU32       NextTotalWidth;\n    ImU16       Spacing;\n    ImU16       OffsetIcon;         // Always zero for now\n    ImU16       OffsetLabel;        // Offsets are locked in Update()\n    ImU16       OffsetShortcut;\n    ImU16       OffsetMark;\n    ImU16       Widths[4];          // Width of:   Icon, Label, Shortcut, Mark  (accumulators for current frame)\n\n    ImGuiMenuColumns() { memset(this, 0, sizeof(*this)); }\n    void        Update(float spacing, bool window_reappearing);\n    float       DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark);\n    void        CalcNextTotalWidth(bool update_offsets);\n};\n\n// Internal temporary state for deactivating InputText() instances.\nstruct IMGUI_API ImGuiInputTextDeactivatedState\n{\n    ImGuiID            ID;              // widget id owning the text state (which just got deactivated)\n    ImVector<char>     TextA;           // text buffer\n\n    ImGuiInputTextDeactivatedState()    { memset(this, 0, sizeof(*this)); }\n    void    ClearFreeMemory()           { ID = 0; TextA.clear(); }\n};\n// Internal state of the currently focused/edited text input box\n// For a given item ID, access with ImGui::GetInputTextState()\nstruct IMGUI_API ImGuiInputTextState\n{\n    ImGuiContext*           Ctx;                    // parent UI context (needs to be set explicitly by parent).\n    ImGuiID                 ID;                     // widget id owning the text state\n    int                     CurLenW, CurLenA;       // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 length is valid even if TextA is not.\n    ImVector<ImWchar>       TextW;                  // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer.\n    ImVector<char>          TextA;                  // temporary UTF8 buffer for callbacks and other operations. this is not updated in every code-path! size=capacity.\n    ImVector<char>          InitialTextA;           // backup of end-user buffer at the time of focus (in UTF-8, unaltered)\n    bool                    TextAIsValid;           // temporary UTF8 buffer is not initially valid before we make the widget active (until then we pull the data from user argument)\n    int                     BufCapacityA;           // end-user buffer capacity\n    float                   ScrollX;                // horizontal scrolling/offset\n    ImStb::STB_TexteditState Stb;                   // state for stb_textedit.h\n    float                   CursorAnim;             // timer for cursor blink, reset on every user action so the cursor reappears immediately\n    bool                    CursorFollow;           // set when we want scrolling to follow the current cursor position (not always!)\n    bool                    SelectedAllMouseLock;   // after a double-click to select all, we ignore further mouse drags to update selection\n    bool                    Edited;                 // edited this frame\n    ImGuiInputTextFlags     Flags;                  // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set.\n\n    ImGuiInputTextState()                   { memset(this, 0, sizeof(*this)); }\n    void        ClearText()                 { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); }\n    void        ClearFreeMemory()           { TextW.clear(); TextA.clear(); InitialTextA.clear(); }\n    int         GetUndoAvailCount() const   { return Stb.undostate.undo_point; }\n    int         GetRedoAvailCount() const   { return STB_TEXTEDIT_UNDOSTATECOUNT - Stb.undostate.redo_point; }\n    void        OnKeyPressed(int key);      // Cannot be inline because we call in code in stb_textedit.h implementation\n\n    // Cursor & Selection\n    void        CursorAnimReset()           { CursorAnim = -0.30f; }                                   // After a user-input the cursor stays on for a while without blinking\n    void        CursorClamp()               { Stb.cursor = ImMin(Stb.cursor, CurLenW); Stb.select_start = ImMin(Stb.select_start, CurLenW); Stb.select_end = ImMin(Stb.select_end, CurLenW); }\n    bool        HasSelection() const        { return Stb.select_start != Stb.select_end; }\n    void        ClearSelection()            { Stb.select_start = Stb.select_end = Stb.cursor; }\n    int         GetCursorPos() const        { return Stb.cursor; }\n    int         GetSelectionStart() const   { return Stb.select_start; }\n    int         GetSelectionEnd() const     { return Stb.select_end; }\n    void        SelectAll()                 { Stb.select_start = 0; Stb.cursor = Stb.select_end = CurLenW; Stb.has_preferred_x = 0; }\n};\n\n// Storage for current popup stack\nstruct ImGuiPopupData\n{\n    ImGuiID             PopupId;        // Set on OpenPopup()\n    ImGuiWindow*        Window;         // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup()\n    ImGuiWindow*        BackupNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close\n    int                 ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as \"not any of layers\" value\n    int                 OpenFrameCount; // Set on OpenPopup()\n    ImGuiID             OpenParentId;   // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items)\n    ImVec2              OpenPopupPos;   // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse)\n    ImVec2              OpenMousePos;   // Set on OpenPopup(), copy of mouse position at the time of opening popup\n\n    ImGuiPopupData()    { memset(this, 0, sizeof(*this)); ParentNavLayer = OpenFrameCount = -1; }\n};\n\nenum ImGuiNextWindowDataFlags_\n{\n    ImGuiNextWindowDataFlags_None               = 0,\n    ImGuiNextWindowDataFlags_HasPos             = 1 << 0,\n    ImGuiNextWindowDataFlags_HasSize            = 1 << 1,\n    ImGuiNextWindowDataFlags_HasContentSize     = 1 << 2,\n    ImGuiNextWindowDataFlags_HasCollapsed       = 1 << 3,\n    ImGuiNextWindowDataFlags_HasSizeConstraint  = 1 << 4,\n    ImGuiNextWindowDataFlags_HasFocus           = 1 << 5,\n    ImGuiNextWindowDataFlags_HasBgAlpha         = 1 << 6,\n    ImGuiNextWindowDataFlags_HasScroll          = 1 << 7,\n    ImGuiNextWindowDataFlags_HasChildFlags      = 1 << 8,\n};\n\n// Storage for SetNexWindow** functions\nstruct ImGuiNextWindowData\n{\n    ImGuiNextWindowDataFlags    Flags;\n    ImGuiCond                   PosCond;\n    ImGuiCond                   SizeCond;\n    ImGuiCond                   CollapsedCond;\n    ImVec2                      PosVal;\n    ImVec2                      PosPivotVal;\n    ImVec2                      SizeVal;\n    ImVec2                      ContentSizeVal;\n    ImVec2                      ScrollVal;\n    ImGuiChildFlags             ChildFlags;\n    bool                        CollapsedVal;\n    ImRect                      SizeConstraintRect;\n    ImGuiSizeCallback           SizeCallback;\n    void*                       SizeCallbackUserData;\n    float                       BgAlphaVal;             // Override background alpha\n    ImVec2                      MenuBarOffsetMinVal;    // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?)\n\n    ImGuiNextWindowData()       { memset(this, 0, sizeof(*this)); }\n    inline void ClearFlags()    { Flags = ImGuiNextWindowDataFlags_None; }\n};\n\n// Multi-Selection item index or identifier when using SetNextItemSelectionUserData()/BeginMultiSelect()\n// (Most users are likely to use this store an item INDEX but this may be used to store a POINTER as well.)\ntypedef ImS64 ImGuiSelectionUserData;\n\nenum ImGuiNextItemDataFlags_\n{\n    ImGuiNextItemDataFlags_None     = 0,\n    ImGuiNextItemDataFlags_HasWidth = 1 << 0,\n    ImGuiNextItemDataFlags_HasOpen  = 1 << 1,\n};\n\nstruct ImGuiNextItemData\n{\n    ImGuiNextItemDataFlags      Flags;\n    ImGuiItemFlags              ItemFlags;          // Currently only tested/used for ImGuiItemFlags_AllowOverlap.\n    // Non-flags members are NOT cleared by ItemAdd() meaning they are still valid during NavProcessItem()\n    float                       Width;              // Set by SetNextItemWidth()\n    ImGuiSelectionUserData      SelectionUserData;  // Set by SetNextItemSelectionUserData() (note that NULL/0 is a valid value, we use -1 == ImGuiSelectionUserData_Invalid to mark invalid values)\n    ImGuiCond                   OpenCond;\n    bool                        OpenVal;            // Set by SetNextItemOpen()\n\n    ImGuiNextItemData()         { memset(this, 0, sizeof(*this)); SelectionUserData = -1; }\n    inline void ClearFlags()    { Flags = ImGuiNextItemDataFlags_None; ItemFlags = ImGuiItemFlags_None; } // Also cleared manually by ItemAdd()!\n};\n\n// Status storage for the last submitted item\nstruct ImGuiLastItemData\n{\n    ImGuiID                 ID;\n    ImGuiItemFlags          InFlags;            // See ImGuiItemFlags_\n    ImGuiItemStatusFlags    StatusFlags;        // See ImGuiItemStatusFlags_\n    ImRect                  Rect;               // Full rectangle\n    ImRect                  NavRect;            // Navigation scoring rectangle (not displayed)\n    ImRect                  DisplayRect;        // Display rectangle (only if ImGuiItemStatusFlags_HasDisplayRect is set)\n\n    ImGuiLastItemData()     { memset(this, 0, sizeof(*this)); }\n};\n\n// Store data emitted by TreeNode() for usage by TreePop() to implement ImGuiTreeNodeFlags_NavLeftJumpsBackHere.\n// This is the minimum amount of data that we need to perform the equivalent of NavApplyItemToResult() and which we can't infer in TreePop()\n// Only stored when the node is a potential candidate for landing on a Left arrow jump.\nstruct ImGuiNavTreeNodeData\n{\n    ImGuiID                 ID;\n    ImGuiItemFlags          InFlags;\n    ImRect                  NavRect;\n};\n\nstruct IMGUI_API ImGuiStackSizes\n{\n    short   SizeOfIDStack;\n    short   SizeOfColorStack;\n    short   SizeOfStyleVarStack;\n    short   SizeOfFontStack;\n    short   SizeOfFocusScopeStack;\n    short   SizeOfGroupStack;\n    short   SizeOfItemFlagsStack;\n    short   SizeOfBeginPopupStack;\n    short   SizeOfDisabledStack;\n\n    ImGuiStackSizes() { memset(this, 0, sizeof(*this)); }\n    void SetToContextState(ImGuiContext* ctx);\n    void CompareWithContextState(ImGuiContext* ctx);\n};\n\n// Data saved for each window pushed into the stack\nstruct ImGuiWindowStackData\n{\n    ImGuiWindow*        Window;\n    ImGuiLastItemData   ParentLastItemDataBackup;\n    ImGuiStackSizes     StackSizesOnBegin;      // Store size of various stacks for asserting\n};\n\nstruct ImGuiShrinkWidthItem\n{\n    int         Index;\n    float       Width;\n    float       InitialWidth;\n};\n\nstruct ImGuiPtrOrIndex\n{\n    void*       Ptr;            // Either field can be set, not both. e.g. Dock node tab bars are loose while BeginTabBar() ones are in a pool.\n    int         Index;          // Usually index in a main pool.\n\n    ImGuiPtrOrIndex(void* ptr)  { Ptr = ptr; Index = -1; }\n    ImGuiPtrOrIndex(int index)  { Ptr = NULL; Index = index; }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Inputs support\n//-----------------------------------------------------------------------------\n\n// Bit array for named keys\ntypedef ImBitArray<ImGuiKey_NamedKey_COUNT, -ImGuiKey_NamedKey_BEGIN>    ImBitArrayForNamedKeys;\n\n// [Internal] Key ranges\n#define ImGuiKey_LegacyNativeKey_BEGIN  0\n#define ImGuiKey_LegacyNativeKey_END    512\n#define ImGuiKey_Keyboard_BEGIN         (ImGuiKey_NamedKey_BEGIN)\n#define ImGuiKey_Keyboard_END           (ImGuiKey_GamepadStart)\n#define ImGuiKey_Gamepad_BEGIN          (ImGuiKey_GamepadStart)\n#define ImGuiKey_Gamepad_END            (ImGuiKey_GamepadRStickDown + 1)\n#define ImGuiKey_Mouse_BEGIN            (ImGuiKey_MouseLeft)\n#define ImGuiKey_Mouse_END              (ImGuiKey_MouseWheelY + 1)\n#define ImGuiKey_Aliases_BEGIN          (ImGuiKey_Mouse_BEGIN)\n#define ImGuiKey_Aliases_END            (ImGuiKey_Mouse_END)\n\n// [Internal] Named shortcuts for Navigation\n#define ImGuiKey_NavKeyboardTweakSlow   ImGuiMod_Ctrl\n#define ImGuiKey_NavKeyboardTweakFast   ImGuiMod_Shift\n#define ImGuiKey_NavGamepadTweakSlow    ImGuiKey_GamepadL1\n#define ImGuiKey_NavGamepadTweakFast    ImGuiKey_GamepadR1\n#define ImGuiKey_NavGamepadActivate     ImGuiKey_GamepadFaceDown\n#define ImGuiKey_NavGamepadCancel       ImGuiKey_GamepadFaceRight\n#define ImGuiKey_NavGamepadMenu         ImGuiKey_GamepadFaceLeft\n#define ImGuiKey_NavGamepadInput        ImGuiKey_GamepadFaceUp\n\nenum ImGuiInputEventType\n{\n    ImGuiInputEventType_None = 0,\n    ImGuiInputEventType_MousePos,\n    ImGuiInputEventType_MouseWheel,\n    ImGuiInputEventType_MouseButton,\n    ImGuiInputEventType_Key,\n    ImGuiInputEventType_Text,\n    ImGuiInputEventType_Focus,\n    ImGuiInputEventType_COUNT\n};\n\nenum ImGuiInputSource\n{\n    ImGuiInputSource_None = 0,\n    ImGuiInputSource_Mouse,         // Note: may be Mouse or TouchScreen or Pen. See io.MouseSource to distinguish them.\n    ImGuiInputSource_Keyboard,\n    ImGuiInputSource_Gamepad,\n    ImGuiInputSource_Clipboard,     // Currently only used by InputText()\n    ImGuiInputSource_COUNT\n};\n\n// FIXME: Structures in the union below need to be declared as anonymous unions appears to be an extension?\n// Using ImVec2() would fail on Clang 'union member 'MousePos' has a non-trivial default constructor'\nstruct ImGuiInputEventMousePos      { float PosX, PosY; ImGuiMouseSource MouseSource; };\nstruct ImGuiInputEventMouseWheel    { float WheelX, WheelY; ImGuiMouseSource MouseSource; };\nstruct ImGuiInputEventMouseButton   { int Button; bool Down; ImGuiMouseSource MouseSource; };\nstruct ImGuiInputEventKey           { ImGuiKey Key; bool Down; float AnalogValue; };\nstruct ImGuiInputEventText          { unsigned int Char; };\nstruct ImGuiInputEventAppFocused    { bool Focused; };\n\nstruct ImGuiInputEvent\n{\n    ImGuiInputEventType             Type;\n    ImGuiInputSource                Source;\n    ImU32                           EventId;        // Unique, sequential increasing integer to identify an event (if you need to correlate them to other data).\n    union\n    {\n        ImGuiInputEventMousePos     MousePos;       // if Type == ImGuiInputEventType_MousePos\n        ImGuiInputEventMouseWheel   MouseWheel;     // if Type == ImGuiInputEventType_MouseWheel\n        ImGuiInputEventMouseButton  MouseButton;    // if Type == ImGuiInputEventType_MouseButton\n        ImGuiInputEventKey          Key;            // if Type == ImGuiInputEventType_Key\n        ImGuiInputEventText         Text;           // if Type == ImGuiInputEventType_Text\n        ImGuiInputEventAppFocused   AppFocused;     // if Type == ImGuiInputEventType_Focus\n    };\n    bool                            AddedByTestEngine;\n\n    ImGuiInputEvent() { memset(this, 0, sizeof(*this)); }\n};\n\n// Input function taking an 'ImGuiID owner_id' argument defaults to (ImGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior.\n#define ImGuiKeyOwner_Any           ((ImGuiID)0)    // Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.\n#define ImGuiKeyOwner_None          ((ImGuiID)-1)   // Require key to have no owner.\n\ntypedef ImS16 ImGuiKeyRoutingIndex;\n\n// Routing table entry (sizeof() == 16 bytes)\nstruct ImGuiKeyRoutingData\n{\n    ImGuiKeyRoutingIndex            NextEntryIndex;\n    ImU16                           Mods;               // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. ImGuiMod_Shortcut is already translated to Ctrl/Super.\n    ImU8                            RoutingNextScore;   // Lower is better (0: perfect score)\n    ImGuiID                         RoutingCurr;\n    ImGuiID                         RoutingNext;\n\n    ImGuiKeyRoutingData()           { NextEntryIndex = -1; Mods = 0; RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_None; }\n};\n\n// Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching.\n// Stored in main context (1 instance)\nstruct ImGuiKeyRoutingTable\n{\n    ImGuiKeyRoutingIndex            Index[ImGuiKey_NamedKey_COUNT]; // Index of first entry in Entries[]\n    ImVector<ImGuiKeyRoutingData>   Entries;\n    ImVector<ImGuiKeyRoutingData>   EntriesNext;                    // Double-buffer to avoid reallocation (could use a shared buffer)\n\n    ImGuiKeyRoutingTable()          { Clear(); }\n    void Clear()                    { for (int n = 0; n < IM_ARRAYSIZE(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); }\n};\n\n// This extends ImGuiKeyData but only for named keys (legacy keys don't support the new features)\n// Stored in main context (1 per named key). In the future it might be merged into ImGuiKeyData.\nstruct ImGuiKeyOwnerData\n{\n    ImGuiID     OwnerCurr;\n    ImGuiID     OwnerNext;\n    bool        LockThisFrame;      // Reading this key requires explicit owner id (until end of frame). Set by ImGuiInputFlags_LockThisFrame.\n    bool        LockUntilRelease;   // Reading this key requires explicit owner id (until key is released). Set by ImGuiInputFlags_LockUntilRelease. When this is true LockThisFrame is always true as well.\n\n    ImGuiKeyOwnerData()             { OwnerCurr = OwnerNext = ImGuiKeyOwner_None; LockThisFrame = LockUntilRelease = false; }\n};\n\n// Flags for extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner()\n// Don't mistake with ImGuiInputTextFlags! (for ImGui::InputText() function)\nenum ImGuiInputFlags_\n{\n    // Flags for IsKeyPressed(), IsMouseClicked(), Shortcut()\n    ImGuiInputFlags_None                = 0,\n    ImGuiInputFlags_Repeat              = 1 << 0,   // Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1.\n    ImGuiInputFlags_RepeatRateDefault   = 1 << 1,   // Repeat rate: Regular (default)\n    ImGuiInputFlags_RepeatRateNavMove   = 1 << 2,   // Repeat rate: Fast\n    ImGuiInputFlags_RepeatRateNavTweak  = 1 << 3,   // Repeat rate: Faster\n    ImGuiInputFlags_RepeatRateMask_     = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak,\n\n    // Flags for SetItemKeyOwner()\n    ImGuiInputFlags_CondHovered         = 1 << 4,   // Only set if item is hovered (default to both)\n    ImGuiInputFlags_CondActive          = 1 << 5,   // Only set if item is active (default to both)\n    ImGuiInputFlags_CondDefault_        = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,\n    ImGuiInputFlags_CondMask_           = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive,\n\n    // Flags for SetKeyOwner(), SetItemKeyOwner()\n    ImGuiInputFlags_LockThisFrame       = 1 << 6,   // Access to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code.\n    ImGuiInputFlags_LockUntilRelease    = 1 << 7,   // Access to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code.\n\n    // Routing policies for Shortcut() + low-level SetShortcutRouting()\n    // - The general idea is that several callers register interest in a shortcut, and only one owner gets it.\n    // - When a policy (other than _RouteAlways) is set, Shortcut() will register itself with SetShortcutRouting(),\n    //   allowing the system to decide where to route the input among other route-aware calls.\n    // - Shortcut() uses ImGuiInputFlags_RouteFocused by default: meaning that a simple Shortcut() poll\n    //   will register a route and only succeed when parent window is in the focus stack and if no-one\n    //   with a higher priority is claiming the shortcut.\n    // - Using ImGuiInputFlags_RouteAlways is roughly equivalent to doing e.g. IsKeyPressed(key) + testing mods.\n    // - Priorities: GlobalHigh > Focused (when owner is active item) > Global > Focused (when focused window) > GlobalLow.\n    // - Can select only 1 policy among all available.\n    ImGuiInputFlags_RouteFocused        = 1 << 8,   // (Default) Register focused route: Accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window.\n    ImGuiInputFlags_RouteGlobalLow      = 1 << 9,   // Register route globally (lowest priority: unless a focused window or active item registered the route) -> recommended Global priority.\n    ImGuiInputFlags_RouteGlobal         = 1 << 10,  // Register route globally (medium priority: unless an active item registered the route, e.g. CTRL+A registered by InputText).\n    ImGuiInputFlags_RouteGlobalHigh     = 1 << 11,  // Register route globally (highest priority: unlikely you need to use that: will interfere with every active items)\n    ImGuiInputFlags_RouteMask_          = ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteGlobalLow | ImGuiInputFlags_RouteGlobalHigh, // _Always not part of this!\n    ImGuiInputFlags_RouteAlways         = 1 << 12,  // Do not register route, poll keys directly.\n    ImGuiInputFlags_RouteUnlessBgFocused= 1 << 13,  // Global routes will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications.\n    ImGuiInputFlags_RouteExtraMask_     = ImGuiInputFlags_RouteAlways | ImGuiInputFlags_RouteUnlessBgFocused,\n\n    // [Internal] Mask of which function support which flags\n    ImGuiInputFlags_SupportedByIsKeyPressed     = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_,\n    ImGuiInputFlags_SupportedByShortcut         = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RouteMask_ | ImGuiInputFlags_RouteExtraMask_,\n    ImGuiInputFlags_SupportedBySetKeyOwner      = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease,\n    ImGuiInputFlags_SupportedBySetItemKeyOwner  = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_,\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Clipper support\n//-----------------------------------------------------------------------------\n\n// Note that Max is exclusive, so perhaps should be using a Begin/End convention.\nstruct ImGuiListClipperRange\n{\n    int     Min;\n    int     Max;\n    bool    PosToIndexConvert;      // Begin/End are absolute position (will be converted to indices later)\n    ImS8    PosToIndexOffsetMin;    // Add to Min after converting to indices\n    ImS8    PosToIndexOffsetMax;    // Add to Min after converting to indices\n\n    static ImGuiListClipperRange    FromIndices(int min, int max)                               { ImGuiListClipperRange r = { min, max, false, 0, 0 }; return r; }\n    static ImGuiListClipperRange    FromPositions(float y1, float y2, int off_min, int off_max) { ImGuiListClipperRange r = { (int)y1, (int)y2, true, (ImS8)off_min, (ImS8)off_max }; return r; }\n};\n\n// Temporary clipper data, buffers shared/reused between instances\nstruct ImGuiListClipperData\n{\n    ImGuiListClipper*               ListClipper;\n    float                           LossynessOffset;\n    int                             StepNo;\n    int                             ItemsFrozen;\n    ImVector<ImGuiListClipperRange> Ranges;\n\n    ImGuiListClipperData()          { memset(this, 0, sizeof(*this)); }\n    void                            Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Navigation support\n//-----------------------------------------------------------------------------\n\nenum ImGuiActivateFlags_\n{\n    ImGuiActivateFlags_None                 = 0,\n    ImGuiActivateFlags_PreferInput          = 1 << 0,       // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default for Enter key.\n    ImGuiActivateFlags_PreferTweak          = 1 << 1,       // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default for Space key and if keyboard is not used.\n    ImGuiActivateFlags_TryToPreserveState   = 1 << 2,       // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection)\n};\n\n// Early work-in-progress API for ScrollToItem()\nenum ImGuiScrollFlags_\n{\n    ImGuiScrollFlags_None                   = 0,\n    ImGuiScrollFlags_KeepVisibleEdgeX       = 1 << 0,       // If item is not visible: scroll as little as possible on X axis to bring item back into view [default for X axis]\n    ImGuiScrollFlags_KeepVisibleEdgeY       = 1 << 1,       // If item is not visible: scroll as little as possible on Y axis to bring item back into view [default for Y axis for windows that are already visible]\n    ImGuiScrollFlags_KeepVisibleCenterX     = 1 << 2,       // If item is not visible: scroll to make the item centered on X axis [rarely used]\n    ImGuiScrollFlags_KeepVisibleCenterY     = 1 << 3,       // If item is not visible: scroll to make the item centered on Y axis\n    ImGuiScrollFlags_AlwaysCenterX          = 1 << 4,       // Always center the result item on X axis [rarely used]\n    ImGuiScrollFlags_AlwaysCenterY          = 1 << 5,       // Always center the result item on Y axis [default for Y axis for appearing window)\n    ImGuiScrollFlags_NoScrollParent         = 1 << 6,       // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to).\n    ImGuiScrollFlags_MaskX_                 = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX,\n    ImGuiScrollFlags_MaskY_                 = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY,\n};\n\nenum ImGuiNavHighlightFlags_\n{\n    ImGuiNavHighlightFlags_None             = 0,\n    ImGuiNavHighlightFlags_TypeDefault      = 1 << 0,\n    ImGuiNavHighlightFlags_TypeThin         = 1 << 1,\n    ImGuiNavHighlightFlags_AlwaysDraw       = 1 << 2,       // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse.\n    ImGuiNavHighlightFlags_NoRounding       = 1 << 3,\n};\n\nenum ImGuiNavMoveFlags_\n{\n    ImGuiNavMoveFlags_None                  = 0,\n    ImGuiNavMoveFlags_LoopX                 = 1 << 0,   // On failed request, restart from opposite side\n    ImGuiNavMoveFlags_LoopY                 = 1 << 1,\n    ImGuiNavMoveFlags_WrapX                 = 1 << 2,   // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left)\n    ImGuiNavMoveFlags_WrapY                 = 1 << 3,   // This is not super useful but provided for completeness\n    ImGuiNavMoveFlags_WrapMask_             = ImGuiNavMoveFlags_LoopX | ImGuiNavMoveFlags_LoopY | ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_WrapY,\n    ImGuiNavMoveFlags_AllowCurrentNavId     = 1 << 4,   // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place)\n    ImGuiNavMoveFlags_AlsoScoreVisibleSet   = 1 << 5,   // Store alternate result in NavMoveResultLocalVisible that only comprise elements that are already fully visible (used by PageUp/PageDown)\n    ImGuiNavMoveFlags_ScrollToEdgeY         = 1 << 6,   // Force scrolling to min/max (used by Home/End) // FIXME-NAV: Aim to remove or reword, probably unnecessary\n    ImGuiNavMoveFlags_Forwarded             = 1 << 7,\n    ImGuiNavMoveFlags_DebugNoResult         = 1 << 8,   // Dummy scoring for debug purpose, don't apply result\n    ImGuiNavMoveFlags_FocusApi              = 1 << 9,   // Requests from focus API can land/focus/activate items even if they are marked with _NoTabStop (see NavProcessItemForTabbingRequest() for details)\n    ImGuiNavMoveFlags_IsTabbing             = 1 << 10,  // == Focus + Activate if item is Inputable + DontChangeNavHighlight\n    ImGuiNavMoveFlags_IsPageMove            = 1 << 11,  // Identify a PageDown/PageUp request.\n    ImGuiNavMoveFlags_Activate              = 1 << 12,  // Activate/select target item.\n    ImGuiNavMoveFlags_NoSelect              = 1 << 13,  // Don't trigger selection by not setting g.NavJustMovedTo\n    ImGuiNavMoveFlags_NoSetNavHighlight     = 1 << 14,  // Do not alter the visible state of keyboard vs mouse nav highlight\n};\n\nenum ImGuiNavLayer\n{\n    ImGuiNavLayer_Main  = 0,    // Main scrolling layer\n    ImGuiNavLayer_Menu  = 1,    // Menu layer (access with Alt)\n    ImGuiNavLayer_COUNT\n};\n\nstruct ImGuiNavItemData\n{\n    ImGuiWindow*        Window;         // Init,Move    // Best candidate window (result->ItemWindow->RootWindowForNav == request->Window)\n    ImGuiID             ID;             // Init,Move    // Best candidate item ID\n    ImGuiID             FocusScopeId;   // Init,Move    // Best candidate focus scope ID\n    ImRect              RectRel;        // Init,Move    // Best candidate bounding box in window relative space\n    ImGuiItemFlags      InFlags;        // ????,Move    // Best candidate item flags\n    ImGuiSelectionUserData SelectionUserData;//I+Mov    // Best candidate SetNextItemSelectionData() value.\n    float               DistBox;        //      Move    // Best candidate box distance to current NavId\n    float               DistCenter;     //      Move    // Best candidate center distance to current NavId\n    float               DistAxial;      //      Move    // Best candidate axial distance to current NavId\n\n    ImGuiNavItemData()  { Clear(); }\n    void Clear()        { Window = NULL; ID = FocusScopeId = 0; InFlags = 0; SelectionUserData = -1; DistBox = DistCenter = DistAxial = FLT_MAX; }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Typing-select support\n//-----------------------------------------------------------------------------\n\n// Flags for GetTypingSelectRequest()\nenum ImGuiTypingSelectFlags_\n{\n    ImGuiTypingSelectFlags_None                 = 0,\n    ImGuiTypingSelectFlags_AllowBackspace       = 1 << 0,   // Backspace to delete character inputs. If using: ensure GetTypingSelectRequest() is not called more than once per frame (filter by e.g. focus state)\n    ImGuiTypingSelectFlags_AllowSingleCharMode  = 1 << 1,   // Allow \"single char\" search mode which is activated when pressing the same character multiple times.\n};\n\n// Returned by GetTypingSelectRequest(), designed to eventually be public.\nstruct IMGUI_API ImGuiTypingSelectRequest\n{\n    ImGuiTypingSelectFlags  Flags;              // Flags passed to GetTypingSelectRequest()\n    int                     SearchBufferLen;\n    const char*             SearchBuffer;       // Search buffer contents (use full string. unless SingleCharMode is set, in which case use SingleCharSize).\n    bool                    SelectRequest;      // Set when buffer was modified this frame, requesting a selection.\n    bool                    SingleCharMode;     // Notify when buffer contains same character repeated, to implement special mode. In this situation it preferred to not display any on-screen search indication.\n    ImS8                    SingleCharSize;     // Length in bytes of first letter codepoint (1 for ascii, 2-4 for UTF-8). If (SearchBufferLen==RepeatCharSize) only 1 letter has been input.\n};\n\n// Storage for GetTypingSelectRequest()\nstruct IMGUI_API ImGuiTypingSelectState\n{\n    ImGuiTypingSelectRequest Request;           // User-facing data\n    char            SearchBuffer[64];           // Search buffer: no need to make dynamic as this search is very transient.\n    ImGuiID         FocusScope;\n    int             LastRequestFrame = 0;\n    float           LastRequestTime = 0.0f;\n    bool            SingleCharModeLock = false; // After a certain single char repeat count we lock into SingleCharMode. Two benefits: 1) buffer never fill, 2) we can provide an immediate SingleChar mode without timer elapsing.\n\n    ImGuiTypingSelectState() { memset(this, 0, sizeof(*this)); }\n    void            Clear()  { SearchBuffer[0] = 0; SingleCharModeLock = false; } // We preserve remaining data for easier debugging\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Columns support\n//-----------------------------------------------------------------------------\n\n// Flags for internal's BeginColumns(). Prefix using BeginTable() nowadays!\nenum ImGuiOldColumnFlags_\n{\n    ImGuiOldColumnFlags_None                    = 0,\n    ImGuiOldColumnFlags_NoBorder                = 1 << 0,   // Disable column dividers\n    ImGuiOldColumnFlags_NoResize                = 1 << 1,   // Disable resizing columns when clicking on the dividers\n    ImGuiOldColumnFlags_NoPreserveWidths        = 1 << 2,   // Disable column width preservation when adjusting columns\n    ImGuiOldColumnFlags_NoForceWithinWindow     = 1 << 3,   // Disable forcing columns to fit within window\n    ImGuiOldColumnFlags_GrowParentContentsSize  = 1 << 4,   // (WIP) Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove.\n\n    // Obsolete names (will be removed)\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    ImGuiColumnsFlags_None                      = ImGuiOldColumnFlags_None,\n    ImGuiColumnsFlags_NoBorder                  = ImGuiOldColumnFlags_NoBorder,\n    ImGuiColumnsFlags_NoResize                  = ImGuiOldColumnFlags_NoResize,\n    ImGuiColumnsFlags_NoPreserveWidths          = ImGuiOldColumnFlags_NoPreserveWidths,\n    ImGuiColumnsFlags_NoForceWithinWindow       = ImGuiOldColumnFlags_NoForceWithinWindow,\n    ImGuiColumnsFlags_GrowParentContentsSize    = ImGuiOldColumnFlags_GrowParentContentsSize,\n#endif\n};\n\nstruct ImGuiOldColumnData\n{\n    float               OffsetNorm;             // Column start offset, normalized 0.0 (far left) -> 1.0 (far right)\n    float               OffsetNormBeforeResize;\n    ImGuiOldColumnFlags Flags;                  // Not exposed\n    ImRect              ClipRect;\n\n    ImGuiOldColumnData() { memset(this, 0, sizeof(*this)); }\n};\n\nstruct ImGuiOldColumns\n{\n    ImGuiID             ID;\n    ImGuiOldColumnFlags Flags;\n    bool                IsFirstFrame;\n    bool                IsBeingResized;\n    int                 Current;\n    int                 Count;\n    float               OffMinX, OffMaxX;       // Offsets from HostWorkRect.Min.x\n    float               LineMinY, LineMaxY;\n    float               HostCursorPosY;         // Backup of CursorPos at the time of BeginColumns()\n    float               HostCursorMaxPosX;      // Backup of CursorMaxPos at the time of BeginColumns()\n    ImRect              HostInitialClipRect;    // Backup of ClipRect at the time of BeginColumns()\n    ImRect              HostBackupClipRect;     // Backup of ClipRect during PushColumnsBackground()/PopColumnsBackground()\n    ImRect              HostBackupParentWorkRect;//Backup of WorkRect at the time of BeginColumns()\n    ImVector<ImGuiOldColumnData> Columns;\n    ImDrawListSplitter  Splitter;\n\n    ImGuiOldColumns()   { memset(this, 0, sizeof(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Multi-select support\n//-----------------------------------------------------------------------------\n\n// We always assume that -1 is an invalid value (which works for indices and pointers)\n#define ImGuiSelectionUserData_Invalid        ((ImGuiSelectionUserData)-1)\n\n#ifdef IMGUI_HAS_MULTI_SELECT\n// <this is filled in 'range_select' branch>\n#endif // #ifdef IMGUI_HAS_MULTI_SELECT\n\n//-----------------------------------------------------------------------------\n// [SECTION] Docking support\n//-----------------------------------------------------------------------------\n\n#ifdef IMGUI_HAS_DOCK\n// <this is filled in 'docking' branch>\n#endif // #ifdef IMGUI_HAS_DOCK\n\n//-----------------------------------------------------------------------------\n// [SECTION] Viewport support\n//-----------------------------------------------------------------------------\n\n// ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!)\n// Every instance of ImGuiViewport is in fact a ImGuiViewportP.\nstruct ImGuiViewportP : public ImGuiViewport\n{\n    int                 BgFgDrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used\n    ImDrawList*         BgFgDrawLists[2];       // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays.\n    ImDrawData          DrawDataP;\n    ImDrawDataBuilder   DrawDataBuilder;        // Temporary data while building final ImDrawData\n    ImVec2              WorkOffsetMin;          // Work Area: Offset from Pos to top-left corner of Work Area. Generally (0,0) or (0,+main_menu_bar_height). Work Area is Full Area but without menu-bars/status-bars (so WorkArea always fit inside Pos/Size!)\n    ImVec2              WorkOffsetMax;          // Work Area: Offset from Pos+Size to bottom-right corner of Work Area. Generally (0,0) or (0,-status_bar_height).\n    ImVec2              BuildWorkOffsetMin;     // Work Area: Offset being built during current frame. Generally >= 0.0f.\n    ImVec2              BuildWorkOffsetMax;     // Work Area: Offset being built during current frame. Generally <= 0.0f.\n\n    ImGuiViewportP()    { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; }\n    ~ImGuiViewportP()   { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); }\n\n    // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect)\n    ImVec2  CalcWorkRectPos(const ImVec2& off_min) const                            { return ImVec2(Pos.x + off_min.x, Pos.y + off_min.y); }\n    ImVec2  CalcWorkRectSize(const ImVec2& off_min, const ImVec2& off_max) const    { return ImVec2(ImMax(0.0f, Size.x - off_min.x + off_max.x), ImMax(0.0f, Size.y - off_min.y + off_max.y)); }\n    void    UpdateWorkRect()            { WorkPos = CalcWorkRectPos(WorkOffsetMin); WorkSize = CalcWorkRectSize(WorkOffsetMin, WorkOffsetMax); } // Update public fields\n\n    // Helpers to retrieve ImRect (we don't need to store BuildWorkRect as every access tend to change it, hence the code asymmetry)\n    ImRect  GetMainRect() const         { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }\n    ImRect  GetWorkRect() const         { return ImRect(WorkPos.x, WorkPos.y, WorkPos.x + WorkSize.x, WorkPos.y + WorkSize.y); }\n    ImRect  GetBuildWorkRect() const    { ImVec2 pos = CalcWorkRectPos(BuildWorkOffsetMin); ImVec2 size = CalcWorkRectSize(BuildWorkOffsetMin, BuildWorkOffsetMax); return ImRect(pos.x, pos.y, pos.x + size.x, pos.y + size.y); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Settings support\n//-----------------------------------------------------------------------------\n\n// Windows data saved in imgui.ini file\n// Because we never destroy or rename ImGuiWindowSettings, we can store the names in a separate buffer easily.\n// (this is designed to be stored in a ImChunkStream buffer, with the variable-length Name following our structure)\nstruct ImGuiWindowSettings\n{\n    ImGuiID     ID;\n    ImVec2ih    Pos;\n    ImVec2ih    Size;\n    bool        Collapsed;\n    bool        IsChild;\n    bool        WantApply;      // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)\n    bool        WantDelete;     // Set to invalidate/delete the settings entry\n\n    ImGuiWindowSettings()       { memset(this, 0, sizeof(*this)); }\n    char* GetName()             { return (char*)(this + 1); }\n};\n\nstruct ImGuiSettingsHandler\n{\n    const char* TypeName;       // Short description stored in .ini file. Disallowed characters: '[' ']'\n    ImGuiID     TypeHash;       // == ImHashStr(TypeName)\n    void        (*ClearAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);                                // Clear all settings data\n    void        (*ReadInitFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);                                // Read: Called before reading (in registration order)\n    void*       (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name);              // Read: Called when entering into a new ini entry e.g. \"[Window][Name]\"\n    void        (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry\n    void        (*ApplyAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler);                                // Read: Called after reading (in registration order)\n    void        (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf);      // Write: Output every entries into 'out_buf'\n    void*       UserData;\n\n    ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Localization support\n//-----------------------------------------------------------------------------\n\n// This is experimental and not officially supported, it'll probably fall short of features, if/when it does we may backtrack.\nenum ImGuiLocKey : int\n{\n    ImGuiLocKey_VersionStr,\n    ImGuiLocKey_TableSizeOne,\n    ImGuiLocKey_TableSizeAllFit,\n    ImGuiLocKey_TableSizeAllDefault,\n    ImGuiLocKey_TableResetOrder,\n    ImGuiLocKey_WindowingMainMenuBar,\n    ImGuiLocKey_WindowingPopup,\n    ImGuiLocKey_WindowingUntitled,\n    ImGuiLocKey_COUNT\n};\n\nstruct ImGuiLocEntry\n{\n    ImGuiLocKey     Key;\n    const char*     Text;\n};\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] Metrics, Debug Tools\n//-----------------------------------------------------------------------------\n\nenum ImGuiDebugLogFlags_\n{\n    // Event types\n    ImGuiDebugLogFlags_None             = 0,\n    ImGuiDebugLogFlags_EventActiveId    = 1 << 0,\n    ImGuiDebugLogFlags_EventFocus       = 1 << 1,\n    ImGuiDebugLogFlags_EventPopup       = 1 << 2,\n    ImGuiDebugLogFlags_EventNav         = 1 << 3,\n    ImGuiDebugLogFlags_EventClipper     = 1 << 4,\n    ImGuiDebugLogFlags_EventSelection   = 1 << 5,\n    ImGuiDebugLogFlags_EventIO          = 1 << 6,\n    ImGuiDebugLogFlags_EventMask_       = ImGuiDebugLogFlags_EventActiveId  | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO,\n    ImGuiDebugLogFlags_OutputToTTY        = 1 << 10,  // Also send output to TTY\n    ImGuiDebugLogFlags_OutputToTestEngine = 1 << 11,  // Also send output to Test Engine\n};\n\nstruct ImGuiDebugAllocEntry\n{\n    int         FrameCount;\n    ImS16       AllocCount;\n    ImS16       FreeCount;\n};\n\nstruct ImGuiDebugAllocInfo\n{\n    int         TotalAllocCount;            // Number of call to MemAlloc().\n    int         TotalFreeCount;\n    ImS16       LastEntriesIdx;             // Current index in buffer\n    ImGuiDebugAllocEntry LastEntriesBuf[6]; // Track last 6 frames that had allocations\n\n    ImGuiDebugAllocInfo() { memset(this, 0, sizeof(*this)); }\n};\n\nstruct ImGuiMetricsConfig\n{\n    bool        ShowDebugLog = false;\n    bool        ShowIDStackTool = false;\n    bool        ShowWindowsRects = false;\n    bool        ShowWindowsBeginOrder = false;\n    bool        ShowTablesRects = false;\n    bool        ShowDrawCmdMesh = true;\n    bool        ShowDrawCmdBoundingBoxes = true;\n    bool        ShowAtlasTintedWithTextColor = false;\n    int         ShowWindowsRectsType = -1;\n    int         ShowTablesRectsType = -1;\n};\n\nstruct ImGuiStackLevelInfo\n{\n    ImGuiID                 ID;\n    ImS8                    QueryFrameCount;            // >= 1: Query in progress\n    bool                    QuerySuccess;               // Obtained result from DebugHookIdInfo()\n    ImGuiDataType           DataType : 8;\n    char                    Desc[57];                   // Arbitrarily sized buffer to hold a result (FIXME: could replace Results[] with a chunk stream?) FIXME: Now that we added CTRL+C this should be fixed.\n\n    ImGuiStackLevelInfo()   { memset(this, 0, sizeof(*this)); }\n};\n\n// State for ID Stack tool queries\nstruct ImGuiIDStackTool\n{\n    int                     LastActiveFrame;\n    int                     StackLevel;                 // -1: query stack and resize Results, >= 0: individual stack level\n    ImGuiID                 QueryId;                    // ID to query details for\n    ImVector<ImGuiStackLevelInfo> Results;\n    bool                    CopyToClipboardOnCtrlC;\n    float                   CopyToClipboardLastTime;\n\n    ImGuiIDStackTool()      { memset(this, 0, sizeof(*this)); CopyToClipboardLastTime = -FLT_MAX; }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Generic context hooks\n//-----------------------------------------------------------------------------\n\ntypedef void (*ImGuiContextHookCallback)(ImGuiContext* ctx, ImGuiContextHook* hook);\nenum ImGuiContextHookType { ImGuiContextHookType_NewFramePre, ImGuiContextHookType_NewFramePost, ImGuiContextHookType_EndFramePre, ImGuiContextHookType_EndFramePost, ImGuiContextHookType_RenderPre, ImGuiContextHookType_RenderPost, ImGuiContextHookType_Shutdown, ImGuiContextHookType_PendingRemoval_ };\n\nstruct ImGuiContextHook\n{\n    ImGuiID                     HookId;     // A unique ID assigned by AddContextHook()\n    ImGuiContextHookType        Type;\n    ImGuiID                     Owner;\n    ImGuiContextHookCallback    Callback;\n    void*                       UserData;\n\n    ImGuiContextHook()          { memset(this, 0, sizeof(*this)); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiContext (main Dear ImGui context)\n//-----------------------------------------------------------------------------\n\nstruct ImGuiContext\n{\n    bool                    Initialized;\n    bool                    FontAtlasOwnedByContext;            // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it.\n    ImGuiIO                 IO;\n    ImGuiStyle              Style;\n    ImFont*                 Font;                               // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back()\n    float                   FontSize;                           // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window.\n    float                   FontBaseSize;                       // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height.\n    ImDrawListSharedData    DrawListSharedData;\n    double                  Time;\n    int                     FrameCount;\n    int                     FrameCountEnded;\n    int                     FrameCountRendered;\n    bool                    WithinFrameScope;                   // Set by NewFrame(), cleared by EndFrame()\n    bool                    WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed\n    bool                    WithinEndChild;                     // Set within EndChild()\n    bool                    GcCompactAll;                       // Request full GC\n    bool                    TestEngineHookItems;                // Will call test engine hooks: ImGuiTestEngineHook_ItemAdd(), ImGuiTestEngineHook_ItemInfo(), ImGuiTestEngineHook_Log()\n    void*                   TestEngine;                         // Test engine user data\n\n    // Inputs\n    ImVector<ImGuiInputEvent> InputEventsQueue;                 // Input events which will be trickled/written into IO structure.\n    ImVector<ImGuiInputEvent> InputEventsTrail;                 // Past input events processed in NewFrame(). This is to allow domain-specific application to access e.g mouse/pen trail.\n    ImGuiMouseSource        InputEventsNextMouseSource;\n    ImU32                   InputEventsNextEventId;\n\n    // Windows state\n    ImVector<ImGuiWindow*>  Windows;                            // Windows, sorted in display order, back to front\n    ImVector<ImGuiWindow*>  WindowsFocusOrder;                  // Root windows, sorted in focus order, back to front.\n    ImVector<ImGuiWindow*>  WindowsTempSortBuffer;              // Temporary buffer used in EndFrame() to reorder windows so parents are kept before their child\n    ImVector<ImGuiWindowStackData> CurrentWindowStack;\n    ImGuiStorage            WindowsById;                        // Map window's ImGuiID to ImGuiWindow*\n    int                     WindowsActiveCount;                 // Number of unique windows submitted by frame\n    ImVec2                  WindowsHoverPadding;                // Padding around resizable windows for which hovering on counts as hovering the window == ImMax(style.TouchExtraPadding, WINDOWS_HOVER_PADDING)\n    ImGuiWindow*            CurrentWindow;                      // Window being drawn into\n    ImGuiWindow*            HoveredWindow;                      // Window the mouse is hovering. Will typically catch mouse inputs.\n    ImGuiWindow*            HoveredWindowUnderMovingWindow;     // Hovered window ignoring MovingWindow. Only set if MovingWindow is set.\n    ImGuiWindow*            MovingWindow;                       // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow.\n    ImGuiWindow*            WheelingWindow;                     // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window.\n    ImVec2                  WheelingWindowRefMousePos;\n    int                     WheelingWindowStartFrame;           // This may be set one frame before WheelingWindow is != NULL\n    int                     WheelingWindowScrolledFrame;\n    float                   WheelingWindowReleaseTimer;\n    ImVec2                  WheelingWindowWheelRemainder;\n    ImVec2                  WheelingAxisAvg;\n\n    // Item/widgets state and tracking information\n    ImGuiID                 DebugHookIdInfo;                    // Will call core hooks: DebugHookIdInfo() from GetID functions, used by ID Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line]\n    ImGuiID                 HoveredId;                          // Hovered widget, filled during the frame\n    ImGuiID                 HoveredIdPreviousFrame;\n    bool                    HoveredIdAllowOverlap;\n    bool                    HoveredIdDisabled;                  // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0.\n    float                   HoveredIdTimer;                     // Measure contiguous hovering time\n    float                   HoveredIdNotActiveTimer;            // Measure contiguous hovering time where the item has not been active\n    ImGuiID                 ActiveId;                           // Active widget\n    ImGuiID                 ActiveIdIsAlive;                    // Active widget has been seen this frame (we can't use a bool as the ActiveId may change within the frame)\n    float                   ActiveIdTimer;\n    bool                    ActiveIdIsJustActivated;            // Set at the time of activation for one frame\n    bool                    ActiveIdAllowOverlap;               // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always)\n    bool                    ActiveIdNoClearOnFocusLoss;         // Disable losing active id if the active id window gets unfocused.\n    bool                    ActiveIdHasBeenPressedBefore;       // Track whether the active id led to a press (this is to allow changing between PressOnClick and PressOnRelease without pressing twice). Used by range_select branch.\n    bool                    ActiveIdHasBeenEditedBefore;        // Was the value associated to the widget Edited over the course of the Active state.\n    bool                    ActiveIdHasBeenEditedThisFrame;\n    ImVec2                  ActiveIdClickOffset;                // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior)\n    ImGuiWindow*            ActiveIdWindow;\n    ImGuiInputSource        ActiveIdSource;                     // Activating source: ImGuiInputSource_Mouse OR ImGuiInputSource_Keyboard OR ImGuiInputSource_Gamepad\n    int                     ActiveIdMouseButton;\n    ImGuiID                 ActiveIdPreviousFrame;\n    bool                    ActiveIdPreviousFrameIsAlive;\n    bool                    ActiveIdPreviousFrameHasBeenEditedBefore;\n    ImGuiWindow*            ActiveIdPreviousFrameWindow;\n    ImGuiID                 LastActiveId;                       // Store the last non-zero ActiveId, useful for animation.\n    float                   LastActiveIdTimer;                  // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation.\n\n    // [EXPERIMENTAL] Key/Input Ownership + Shortcut Routing system\n    // - The idea is that instead of \"eating\" a given key, we can link to an owner.\n    // - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID.\n    // - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame().\n    ImGuiKeyOwnerData       KeysOwnerData[ImGuiKey_NamedKey_COUNT];\n    ImGuiKeyRoutingTable    KeysRoutingTable;\n    ImU32                   ActiveIdUsingNavDirMask;            // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it)\n    bool                    ActiveIdUsingAllKeyboardKeys;       // Active widget will want to read all keyboard keys inputs. (FIXME: This is a shortcut for not taking ownership of 100+ keys but perhaps best to not have the inconsistency)\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    ImU32                   ActiveIdUsingNavInputMask;          // If you used this. Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);'\n#endif\n\n    // Next window/item data\n    ImGuiID                 CurrentFocusScopeId;                // == g.FocusScopeStack.back()\n    ImGuiItemFlags          CurrentItemFlags;                   // == g.ItemFlagsStack.back()\n    ImGuiID                 DebugLocateId;                      // Storage for DebugLocateItemOnHover() feature: this is read by ItemAdd() so we keep it in a hot/cached location\n    ImGuiNextItemData       NextItemData;                       // Storage for SetNextItem** functions\n    ImGuiLastItemData       LastItemData;                       // Storage for last submitted item (setup by ItemAdd)\n    ImGuiNextWindowData     NextWindowData;                     // Storage for SetNextWindow** functions\n    bool                    DebugShowGroupRects;\n\n    // Shared stacks\n    ImVector<ImGuiColorMod>     ColorStack;                     // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin()\n    ImVector<ImGuiStyleMod>     StyleVarStack;                  // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin()\n    ImVector<ImFont*>           FontStack;                      // Stack for PushFont()/PopFont() - inherited by Begin()\n    ImVector<ImGuiID>           FocusScopeStack;                // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin()\n    ImVector<ImGuiItemFlags>    ItemFlagsStack;                 // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin()\n    ImVector<ImGuiGroupData>    GroupStack;                     // Stack for BeginGroup()/EndGroup() - not inherited by Begin()\n    ImVector<ImGuiPopupData>    OpenPopupStack;                 // Which popups are open (persistent)\n    ImVector<ImGuiPopupData>    BeginPopupStack;                // Which level of BeginPopup() we are in (reset every frame)\n    ImVector<ImGuiNavTreeNodeData> NavTreeNodeStack;            // Stack for TreeNode() when a NavLeft requested is emitted.\n\n    int                     BeginMenuCount;\n\n    // Viewports\n    ImVector<ImGuiViewportP*> Viewports;                        // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData.\n\n    // Gamepad/keyboard Navigation\n    ImGuiWindow*            NavWindow;                          // Focused window for navigation. Could be called 'FocusedWindow'\n    ImGuiID                 NavId;                              // Focused item for navigation\n    ImGuiID                 NavFocusScopeId;                    // Identify a selection scope (selection code often wants to \"clear other items\" when landing on an item of the selection set)\n    ImGuiID                 NavActivateId;                      // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem()\n    ImGuiID                 NavActivateDownId;                  // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_Enter) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0\n    ImGuiID                 NavActivatePressedId;               // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat)\n    ImGuiActivateFlags      NavActivateFlags;\n    ImGuiID                 NavJustMovedToId;                   // Just navigated to this id (result of a successfully MoveRequest).\n    ImGuiID                 NavJustMovedToFocusScopeId;         // Just navigated to this focus scope id (result of a successfully MoveRequest).\n    ImGuiKeyChord           NavJustMovedToKeyMods;\n    ImGuiID                 NavNextActivateId;                  // Set by ActivateItem(), queued until next frame.\n    ImGuiActivateFlags      NavNextActivateFlags;\n    ImGuiInputSource        NavInputSource;                     // Keyboard or Gamepad mode? THIS CAN ONLY BE ImGuiInputSource_Keyboard or ImGuiInputSource_Mouse\n    ImGuiNavLayer           NavLayer;                           // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later.\n    ImGuiSelectionUserData  NavLastValidSelectionUserData;      // Last valid data passed to SetNextItemSelectionUser(), or -1. For current window. Not reset when focusing an item that doesn't have selection data.\n    bool                    NavIdIsAlive;                       // Nav widget has been seen this frame ~~ NavRectRel is valid\n    bool                    NavMousePosDirty;                   // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default)\n    bool                    NavDisableHighlight;                // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover)\n    bool                    NavDisableMouseHover;               // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again.\n\n    // Navigation: Init & Move Requests\n    bool                    NavAnyRequest;                      // ~~ NavMoveRequest || NavInitRequest this is to perform early out in ItemAdd()\n    bool                    NavInitRequest;                     // Init request for appearing window to select first item\n    bool                    NavInitRequestFromMove;\n    ImGuiNavItemData        NavInitResult;                      // Init request result (first item of the window, or one for which SetItemDefaultFocus() was called)\n    bool                    NavMoveSubmitted;                   // Move request submitted, will process result on next NewFrame()\n    bool                    NavMoveScoringItems;                // Move request submitted, still scoring incoming items\n    bool                    NavMoveForwardToNextFrame;\n    ImGuiNavMoveFlags       NavMoveFlags;\n    ImGuiScrollFlags        NavMoveScrollFlags;\n    ImGuiKeyChord           NavMoveKeyMods;\n    ImGuiDir                NavMoveDir;                         // Direction of the move request (left/right/up/down)\n    ImGuiDir                NavMoveDirForDebug;\n    ImGuiDir                NavMoveClipDir;                     // FIXME-NAV: Describe the purpose of this better. Might want to rename?\n    ImRect                  NavScoringRect;                     // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring.\n    ImRect                  NavScoringNoClipRect;               // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted\n    int                     NavScoringDebugCount;               // Metrics for debugging\n    int                     NavTabbingDir;                      // Generally -1 or +1, 0 when tabbing without a nav id\n    int                     NavTabbingCounter;                  // >0 when counting items for tabbing\n    ImGuiNavItemData        NavMoveResultLocal;                 // Best move request candidate within NavWindow\n    ImGuiNavItemData        NavMoveResultLocalVisible;          // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag)\n    ImGuiNavItemData        NavMoveResultOther;                 // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag)\n    ImGuiNavItemData        NavTabbingResultFirst;              // First tabbing request candidate within NavWindow and flattened hierarchy\n\n    // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize)\n    ImGuiKeyChord           ConfigNavWindowingKeyNext;          // = ImGuiMod_Ctrl | ImGuiKey_Tab, for reconfiguration (see #4828)\n    ImGuiKeyChord           ConfigNavWindowingKeyPrev;          // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab\n    ImGuiWindow*            NavWindowingTarget;                 // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most!\n    ImGuiWindow*            NavWindowingTargetAnim;             // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it.\n    ImGuiWindow*            NavWindowingListWindow;             // Internal window actually listing the CTRL+Tab contents\n    float                   NavWindowingTimer;\n    float                   NavWindowingHighlightAlpha;\n    bool                    NavWindowingToggleLayer;\n    ImVec2                  NavWindowingAccumDeltaPos;\n    ImVec2                  NavWindowingAccumDeltaSize;\n\n    // Render\n    float                   DimBgRatio;                         // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list)\n\n    // Drag and Drop\n    bool                    DragDropActive;\n    bool                    DragDropWithinSource;               // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag source.\n    bool                    DragDropWithinTarget;               // Set when within a BeginDragDropXXX/EndDragDropXXX block for a drag target.\n    ImGuiDragDropFlags      DragDropSourceFlags;\n    int                     DragDropSourceFrameCount;\n    int                     DragDropMouseButton;\n    ImGuiPayload            DragDropPayload;\n    ImRect                  DragDropTargetRect;                 // Store rectangle of current target candidate (we favor small targets when overlapping)\n    ImGuiID                 DragDropTargetId;\n    ImGuiDragDropFlags      DragDropAcceptFlags;\n    float                   DragDropAcceptIdCurrRectSurface;    // Target item surface (we resolve overlapping targets by prioritizing the smaller surface)\n    ImGuiID                 DragDropAcceptIdCurr;               // Target item id (set at the time of accepting the payload)\n    ImGuiID                 DragDropAcceptIdPrev;               // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets)\n    int                     DragDropAcceptFrameCount;           // Last time a target expressed a desire to accept the source\n    ImGuiID                 DragDropHoldJustPressedId;          // Set when holding a payload just made ButtonBehavior() return a press.\n    ImVector<unsigned char> DragDropPayloadBufHeap;             // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size\n    unsigned char           DragDropPayloadBufLocal[16];        // Local buffer for small payloads\n\n    // Clipper\n    int                             ClipperTempDataStacked;\n    ImVector<ImGuiListClipperData>  ClipperTempData;\n\n    // Tables\n    ImGuiTable*                     CurrentTable;\n    int                             TablesTempDataStacked;      // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size)\n    ImVector<ImGuiTableTempData>    TablesTempData;             // Temporary table data (buffers reused/shared across instances, support nesting)\n    ImPool<ImGuiTable>              Tables;                     // Persistent table data\n    ImVector<float>                 TablesLastTimeActive;       // Last used timestamp of each tables (SOA, for efficient GC)\n    ImVector<ImDrawChannel>         DrawChannelsTempMergeBuffer;\n\n    // Tab bars\n    ImGuiTabBar*                    CurrentTabBar;\n    ImPool<ImGuiTabBar>             TabBars;\n    ImVector<ImGuiPtrOrIndex>       CurrentTabBarStack;\n    ImVector<ImGuiShrinkWidthItem>  ShrinkWidthBuffer;\n\n    // Hover Delay system\n    ImGuiID                 HoverItemDelayId;\n    ImGuiID                 HoverItemDelayIdPreviousFrame;\n    float                   HoverItemDelayTimer;                // Currently used by IsItemHovered()\n    float                   HoverItemDelayClearTimer;           // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared.\n    ImGuiID                 HoverItemUnlockedStationaryId;      // Mouse has once been stationary on this item. Only reset after departing the item.\n    ImGuiID                 HoverWindowUnlockedStationaryId;    // Mouse has once been stationary on this window. Only reset after departing the window.\n\n    // Mouse state\n    ImGuiMouseCursor        MouseCursor;\n    float                   MouseStationaryTimer;               // Time the mouse has been stationary (with some loose heuristic)\n    ImVec2                  MouseLastValidPos;\n\n    // Widget state\n    ImGuiInputTextState     InputTextState;\n    ImGuiInputTextDeactivatedState InputTextDeactivatedState;\n    ImFont                  InputTextPasswordFont;\n    ImGuiID                 TempInputId;                        // Temporary text input when CTRL+clicking on a slider, etc.\n    ImGuiColorEditFlags     ColorEditOptions;                   // Store user options for color edit widgets\n    ImGuiID                 ColorEditCurrentID;                 // Set temporarily while inside of the parent-most ColorEdit4/ColorPicker4 (because they call each others).\n    ImGuiID                 ColorEditSavedID;                   // ID we are saving/restoring HS for\n    float                   ColorEditSavedHue;                  // Backup of last Hue associated to LastColor, so we can restore Hue in lossy RGB<>HSV round trips\n    float                   ColorEditSavedSat;                  // Backup of last Saturation associated to LastColor, so we can restore Saturation in lossy RGB<>HSV round trips\n    ImU32                   ColorEditSavedColor;                // RGB value with alpha set to 0.\n    ImVec4                  ColorPickerRef;                     // Initial/reference color at the time of opening the color picker.\n    ImGuiComboPreviewData   ComboPreviewData;\n    ImRect                  WindowResizeBorderExpectedRect;     // Expected border rect, switch to relative edit if moving\n    bool                    WindowResizeRelativeMode;\n    float                   SliderGrabClickOffset;\n    float                   SliderCurrentAccum;                 // Accumulated slider delta when using navigation controls.\n    bool                    SliderCurrentAccumDirty;            // Has the accumulated slider delta changed since last time we tried to apply it?\n    bool                    DragCurrentAccumDirty;\n    float                   DragCurrentAccum;                   // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings\n    float                   DragSpeedDefaultRatio;              // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio\n    float                   ScrollbarClickDeltaToGrabCenter;    // Distance between mouse and center of grab box, normalized in parent space. Use storage?\n    float                   DisabledAlphaBackup;                // Backup for style.Alpha for BeginDisabled()\n    short                   DisabledStackSize;\n    short                   LockMarkEdited;\n    short                   TooltipOverrideCount;\n    ImVector<char>          ClipboardHandlerData;               // If no custom clipboard handler is defined\n    ImVector<ImGuiID>       MenusIdSubmittedThisFrame;          // A list of menu IDs that were rendered at least once\n    ImGuiTypingSelectState  TypingSelectState;                  // State for GetTypingSelectRequest()\n\n    // Platform support\n    ImGuiPlatformImeData    PlatformImeData;                    // Data updated by current frame\n    ImGuiPlatformImeData    PlatformImeDataPrev;                // Previous frame data (when changing we will call io.SetPlatformImeDataFn\n\n    // Settings\n    bool                    SettingsLoaded;\n    float                   SettingsDirtyTimer;                 // Save .ini Settings to memory when time reaches zero\n    ImGuiTextBuffer         SettingsIniData;                    // In memory .ini settings\n    ImVector<ImGuiSettingsHandler>      SettingsHandlers;       // List of .ini settings handlers\n    ImChunkStream<ImGuiWindowSettings>  SettingsWindows;        // ImGuiWindow .ini settings entries\n    ImChunkStream<ImGuiTableSettings>   SettingsTables;         // ImGuiTable .ini settings entries\n    ImVector<ImGuiContextHook>          Hooks;                  // Hooks for extensions (e.g. test engine)\n    ImGuiID                             HookIdNext;             // Next available HookId\n\n    // Localization\n    const char*             LocalizationTable[ImGuiLocKey_COUNT];\n\n    // Capture/Logging\n    bool                    LogEnabled;                         // Currently capturing\n    ImGuiLogType            LogType;                            // Capture target\n    ImFileHandle            LogFile;                            // If != NULL log to stdout/ file\n    ImGuiTextBuffer         LogBuffer;                          // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators.\n    const char*             LogNextPrefix;\n    const char*             LogNextSuffix;\n    float                   LogLinePosY;\n    bool                    LogLineFirstItem;\n    int                     LogDepthRef;\n    int                     LogDepthToExpand;\n    int                     LogDepthToExpandDefault;            // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call.\n\n    // Debug Tools\n    ImGuiDebugLogFlags      DebugLogFlags;\n    ImGuiTextBuffer         DebugLogBuf;\n    ImGuiTextIndex          DebugLogIndex;\n    ImU8                    DebugLogClipperAutoDisableFrames;\n    ImU8                    DebugLocateFrames;                  // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above.\n    ImS8                    DebugBeginReturnValueCullDepth;     // Cycle between 0..9 then wrap around.\n    bool                    DebugItemPickerActive;              // Item picker is active (started with DebugStartItemPicker())\n    ImU8                    DebugItemPickerMouseButton;\n    ImGuiID                 DebugItemPickerBreakId;             // Will call IM_DEBUG_BREAK() when encountering this ID\n    ImGuiMetricsConfig      DebugMetricsConfig;\n    ImGuiIDStackTool        DebugIDStackTool;\n    ImGuiDebugAllocInfo     DebugAllocInfo;\n\n    // Misc\n    float                   FramerateSecPerFrame[60];           // Calculate estimate of framerate for user over the last 60 frames..\n    int                     FramerateSecPerFrameIdx;\n    int                     FramerateSecPerFrameCount;\n    float                   FramerateSecPerFrameAccum;\n    int                     WantCaptureMouseNextFrame;          // Explicit capture override via SetNextFrameWantCaptureMouse()/SetNextFrameWantCaptureKeyboard(). Default to -1.\n    int                     WantCaptureKeyboardNextFrame;       // \"\n    int                     WantTextInputNextFrame;\n    ImVector<char>          TempBuffer;                         // Temporary text buffer\n\n    ImGuiContext(ImFontAtlas* shared_font_atlas)\n    {\n        IO.Ctx = this;\n        InputTextState.Ctx = this;\n\n        Initialized = false;\n        FontAtlasOwnedByContext = shared_font_atlas ? false : true;\n        Font = NULL;\n        FontSize = FontBaseSize = 0.0f;\n        IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)();\n        Time = 0.0f;\n        FrameCount = 0;\n        FrameCountEnded = FrameCountRendered = -1;\n        WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false;\n        GcCompactAll = false;\n        TestEngineHookItems = false;\n        TestEngine = NULL;\n\n        InputEventsNextMouseSource = ImGuiMouseSource_Mouse;\n        InputEventsNextEventId = 1;\n\n        WindowsActiveCount = 0;\n        CurrentWindow = NULL;\n        HoveredWindow = NULL;\n        HoveredWindowUnderMovingWindow = NULL;\n        MovingWindow = NULL;\n        WheelingWindow = NULL;\n        WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1;\n        WheelingWindowReleaseTimer = 0.0f;\n\n        DebugHookIdInfo = 0;\n        HoveredId = HoveredIdPreviousFrame = 0;\n        HoveredIdAllowOverlap = false;\n        HoveredIdDisabled = false;\n        HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f;\n        ActiveId = 0;\n        ActiveIdIsAlive = 0;\n        ActiveIdTimer = 0.0f;\n        ActiveIdIsJustActivated = false;\n        ActiveIdAllowOverlap = false;\n        ActiveIdNoClearOnFocusLoss = false;\n        ActiveIdHasBeenPressedBefore = false;\n        ActiveIdHasBeenEditedBefore = false;\n        ActiveIdHasBeenEditedThisFrame = false;\n        ActiveIdClickOffset = ImVec2(-1, -1);\n        ActiveIdWindow = NULL;\n        ActiveIdSource = ImGuiInputSource_None;\n        ActiveIdMouseButton = -1;\n        ActiveIdPreviousFrame = 0;\n        ActiveIdPreviousFrameIsAlive = false;\n        ActiveIdPreviousFrameHasBeenEditedBefore = false;\n        ActiveIdPreviousFrameWindow = NULL;\n        LastActiveId = 0;\n        LastActiveIdTimer = 0.0f;\n\n        ActiveIdUsingNavDirMask = 0x00;\n        ActiveIdUsingAllKeyboardKeys = false;\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n        ActiveIdUsingNavInputMask = 0x00;\n#endif\n\n        CurrentFocusScopeId = 0;\n        CurrentItemFlags = ImGuiItemFlags_None;\n        DebugShowGroupRects = false;\n        BeginMenuCount = 0;\n\n        NavWindow = NULL;\n        NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0;\n        NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0;\n        NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None;\n        NavJustMovedToKeyMods = ImGuiMod_None;\n        NavInputSource = ImGuiInputSource_Keyboard;\n        NavLayer = ImGuiNavLayer_Main;\n        NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid;\n        NavIdIsAlive = false;\n        NavMousePosDirty = false;\n        NavDisableHighlight = true;\n        NavDisableMouseHover = false;\n        NavAnyRequest = false;\n        NavInitRequest = false;\n        NavInitRequestFromMove = false;\n        NavMoveSubmitted = false;\n        NavMoveScoringItems = false;\n        NavMoveForwardToNextFrame = false;\n        NavMoveFlags = ImGuiNavMoveFlags_None;\n        NavMoveScrollFlags = ImGuiScrollFlags_None;\n        NavMoveKeyMods = ImGuiMod_None;\n        NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None;\n        NavScoringDebugCount = 0;\n        NavTabbingDir = 0;\n        NavTabbingCounter = 0;\n\n        ConfigNavWindowingKeyNext = ImGuiMod_Ctrl | ImGuiKey_Tab;\n        ConfigNavWindowingKeyPrev = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab;\n        NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL;\n        NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f;\n        NavWindowingToggleLayer = false;\n\n        DimBgRatio = 0.0f;\n\n        DragDropActive = DragDropWithinSource = DragDropWithinTarget = false;\n        DragDropSourceFlags = ImGuiDragDropFlags_None;\n        DragDropSourceFrameCount = -1;\n        DragDropMouseButton = -1;\n        DragDropTargetId = 0;\n        DragDropAcceptFlags = ImGuiDragDropFlags_None;\n        DragDropAcceptIdCurrRectSurface = 0.0f;\n        DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0;\n        DragDropAcceptFrameCount = -1;\n        DragDropHoldJustPressedId = 0;\n        memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));\n\n        ClipperTempDataStacked = 0;\n\n        CurrentTable = NULL;\n        TablesTempDataStacked = 0;\n        CurrentTabBar = NULL;\n\n        HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;\n        HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;\n\n        MouseCursor = ImGuiMouseCursor_Arrow;\n        MouseStationaryTimer = 0.0f;\n\n        TempInputId = 0;\n        ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;\n        ColorEditCurrentID = ColorEditSavedID = 0;\n        ColorEditSavedHue = ColorEditSavedSat = 0.0f;\n        ColorEditSavedColor = 0;\n        WindowResizeRelativeMode = false;\n        SliderGrabClickOffset = 0.0f;\n        SliderCurrentAccum = 0.0f;\n        SliderCurrentAccumDirty = false;\n        DragCurrentAccumDirty = false;\n        DragCurrentAccum = 0.0f;\n        DragSpeedDefaultRatio = 1.0f / 100.0f;\n        ScrollbarClickDeltaToGrabCenter = 0.0f;\n        DisabledAlphaBackup = 0.0f;\n        DisabledStackSize = 0;\n        LockMarkEdited = 0;\n        TooltipOverrideCount = 0;\n\n        PlatformImeData.InputPos = ImVec2(0.0f, 0.0f);\n        PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission\n\n        SettingsLoaded = false;\n        SettingsDirtyTimer = 0.0f;\n        HookIdNext = 0;\n\n        memset(LocalizationTable, 0, sizeof(LocalizationTable));\n\n        LogEnabled = false;\n        LogType = ImGuiLogType_None;\n        LogNextPrefix = LogNextSuffix = NULL;\n        LogFile = NULL;\n        LogLinePosY = FLT_MAX;\n        LogLineFirstItem = false;\n        LogDepthRef = 0;\n        LogDepthToExpand = LogDepthToExpandDefault = 2;\n\n        DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY;\n        DebugLocateId = 0;\n        DebugLogClipperAutoDisableFrames = 0;\n        DebugLocateFrames = 0;\n        DebugBeginReturnValueCullDepth = -1;\n        DebugItemPickerActive = false;\n        DebugItemPickerMouseButton = ImGuiMouseButton_Left;\n        DebugItemPickerBreakId = 0;\n\n        memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame));\n        FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0;\n        FramerateSecPerFrameAccum = 0.0f;\n        WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1;\n    }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGuiWindowTempData, ImGuiWindow\n//-----------------------------------------------------------------------------\n\n// Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow.\n// (That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered..)\n// (This doesn't need a constructor because we zero-clear it as part of ImGuiWindow and all frame-temporary data are setup on Begin)\nstruct IMGUI_API ImGuiWindowTempData\n{\n    // Layout\n    ImVec2                  CursorPos;              // Current emitting position, in absolute coordinates.\n    ImVec2                  CursorPosPrevLine;\n    ImVec2                  CursorStartPos;         // Initial position after Begin(), generally ~ window position + WindowPadding.\n    ImVec2                  CursorMaxPos;           // Used to implicitly calculate ContentSize at the beginning of next frame, for scrolling range and auto-resize. Always growing during the frame.\n    ImVec2                  IdealMaxPos;            // Used to implicitly calculate ContentSizeIdeal at the beginning of next frame, for auto-resize only. Always growing during the frame.\n    ImVec2                  CurrLineSize;\n    ImVec2                  PrevLineSize;\n    float                   CurrLineTextBaseOffset; // Baseline offset (0.0f by default on a new line, generally == style.FramePadding.y when a framed item has been added).\n    float                   PrevLineTextBaseOffset;\n    bool                    IsSameLine;\n    bool                    IsSetPos;\n    ImVec1                  Indent;                 // Indentation / start position from left of window (increased by TreePush/TreePop, etc.)\n    ImVec1                  ColumnsOffset;          // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API.\n    ImVec1                  GroupOffset;\n    ImVec2                  CursorStartPosLossyness;// Record the loss of precision of CursorStartPos due to really large scrolling amount. This is used by clipper to compensate and fix the most common use case of large scroll area.\n\n    // Keyboard/Gamepad navigation\n    ImGuiNavLayer           NavLayerCurrent;        // Current layer, 0..31 (we currently only use 0..1)\n    short                   NavLayersActiveMask;    // Which layers have been written to (result from previous frame)\n    short                   NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame)\n    bool                    NavIsScrollPushableX;   // Set when current work location may be scrolled horizontally when moving left / right. This is generally always true UNLESS within a column.\n    bool                    NavHideHighlightOneFrame;\n    bool                    NavWindowHasScrollY;    // Set per window when scrolling can be used (== ScrollMax.y > 0.0f)\n\n    // Miscellaneous\n    bool                    MenuBarAppending;       // FIXME: Remove this\n    ImVec2                  MenuBarOffset;          // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs.\n    ImGuiMenuColumns        MenuColumns;            // Simplified columns storage for menu items measurement\n    int                     TreeDepth;              // Current tree depth.\n    ImU32                   TreeJumpToParentOnPopMask; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31.. Could be turned into a ImU64 if necessary.\n    ImVector<ImGuiWindow*>  ChildWindows;\n    ImGuiStorage*           StateStorage;           // Current persistent per-window storage (store e.g. tree node open/close state)\n    ImGuiOldColumns*        CurrentColumns;         // Current columns set\n    int                     CurrentTableIdx;        // Current table index (into g.Tables)\n    ImGuiLayoutType         LayoutType;\n    ImGuiLayoutType         ParentLayoutType;       // Layout type of parent window at the time of Begin()\n\n    // Local parameters stacks\n    // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings.\n    float                   ItemWidth;              // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window).\n    float                   TextWrapPos;            // Current text wrap pos.\n    ImVector<float>         ItemWidthStack;         // Store item widths to restore (attention: .back() is not == ItemWidth)\n    ImVector<float>         TextWrapPosStack;       // Store text wrap pos to restore (attention: .back() is not == TextWrapPos)\n};\n\n// Storage for one window\nstruct IMGUI_API ImGuiWindow\n{\n    ImGuiContext*           Ctx;                                // Parent UI context (needs to be set explicitly by parent).\n    char*                   Name;                               // Window name, owned by the window.\n    ImGuiID                 ID;                                 // == ImHashStr(Name)\n    ImGuiWindowFlags        Flags;                              // See enum ImGuiWindowFlags_\n    ImGuiChildFlags         ChildFlags;                         // Set when window is a child window. See enum ImGuiChildFlags_\n    ImGuiViewportP*         Viewport;                           // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded.\n    ImVec2                  Pos;                                // Position (always rounded-up to nearest pixel)\n    ImVec2                  Size;                               // Current size (==SizeFull or collapsed title bar size)\n    ImVec2                  SizeFull;                           // Size when non collapsed\n    ImVec2                  ContentSize;                        // Size of contents/scrollable client area (calculated from the extents reach of the cursor) from previous frame. Does not include window decoration or window padding.\n    ImVec2                  ContentSizeIdeal;\n    ImVec2                  ContentSizeExplicit;                // Size of contents/scrollable client area explicitly request by the user via SetNextWindowContentSize().\n    ImVec2                  WindowPadding;                      // Window padding at the time of Begin().\n    float                   WindowRounding;                     // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc.\n    float                   WindowBorderSize;                   // Window border size at the time of Begin().\n    float                   DecoOuterSizeX1, DecoOuterSizeY1;   // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin().\n    float                   DecoOuterSizeX2, DecoOuterSizeY2;   // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y).\n    float                   DecoInnerSizeX1, DecoInnerSizeY1;   // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes).\n    int                     NameBufLen;                         // Size of buffer storing Name. May be larger than strlen(Name)!\n    ImGuiID                 MoveId;                             // == window->GetID(\"#MOVE\")\n    ImGuiID                 ChildId;                            // ID of corresponding item in parent window (for navigation to return from child window to parent window)\n    ImVec2                  Scroll;\n    ImVec2                  ScrollMax;\n    ImVec2                  ScrollTarget;                       // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change)\n    ImVec2                  ScrollTargetCenterRatio;            // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered\n    ImVec2                  ScrollTargetEdgeSnapDist;           // 0.0f = no snapping, >0.0f snapping threshold\n    ImVec2                  ScrollbarSizes;                     // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar.\n    bool                    ScrollbarX, ScrollbarY;             // Are scrollbars visible?\n    bool                    Active;                             // Set to true on Begin(), unless Collapsed\n    bool                    WasActive;\n    bool                    WriteAccessed;                      // Set to true when any widget access the current window\n    bool                    Collapsed;                          // Set when collapsing window to become only title-bar\n    bool                    WantCollapseToggle;\n    bool                    SkipItems;                          // Set when items can safely be all clipped (e.g. window not visible or collapsed)\n    bool                    Appearing;                          // Set during the frame where the window is appearing (or re-appearing)\n    bool                    Hidden;                             // Do not display (== HiddenFrames*** > 0)\n    bool                    IsFallbackWindow;                   // Set on the \"Debug##Default\" window.\n    bool                    IsExplicitChild;                    // Set when passed _ChildWindow, left to false by BeginDocked()\n    bool                    HasCloseButton;                     // Set when the window has a close button (p_open != NULL)\n    signed char             ResizeBorderHovered;                // Current border being hovered for resize (-1: none, otherwise 0-3)\n    signed char             ResizeBorderHeld;                   // Current border being held for resize (-1: none, otherwise 0-3)\n    short                   BeginCount;                         // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs)\n    short                   BeginCountPreviousFrame;            // Number of Begin() during the previous frame\n    short                   BeginOrderWithinParent;             // Begin() order within immediate parent window, if we are a child window. Otherwise 0.\n    short                   BeginOrderWithinContext;            // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues.\n    short                   FocusOrder;                         // Order within WindowsFocusOrder[], altered when windows are focused.\n    ImGuiID                 PopupId;                            // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling)\n    ImS8                    AutoFitFramesX, AutoFitFramesY;\n    bool                    AutoFitOnlyGrows;\n    ImGuiDir                AutoPosLastDirection;\n    ImS8                    HiddenFramesCanSkipItems;           // Hide the window for N frames\n    ImS8                    HiddenFramesCannotSkipItems;        // Hide the window for N frames while allowing items to be submitted so we can measure their size\n    ImS8                    HiddenFramesForRenderOnly;          // Hide the window until frame N at Render() time only\n    ImS8                    DisableInputsFrames;                // Disable window interactions for N frames\n    ImGuiCond               SetWindowPosAllowFlags : 8;         // store acceptable condition flags for SetNextWindowPos() use.\n    ImGuiCond               SetWindowSizeAllowFlags : 8;        // store acceptable condition flags for SetNextWindowSize() use.\n    ImGuiCond               SetWindowCollapsedAllowFlags : 8;   // store acceptable condition flags for SetNextWindowCollapsed() use.\n    ImVec2                  SetWindowPosVal;                    // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size)\n    ImVec2                  SetWindowPosPivot;                  // store window pivot for positioning. ImVec2(0, 0) when positioning from top-left corner; ImVec2(0.5f, 0.5f) for centering; ImVec2(1, 1) for bottom right.\n\n    ImVector<ImGuiID>       IDStack;                            // ID stack. ID are hashes seeded with the value at the top of the stack. (In theory this should be in the TempData structure)\n    ImGuiWindowTempData     DC;                                 // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the \"DC\" variable name.\n\n    // The best way to understand what those rectangles are is to use the 'Metrics->Tools->Show Windows Rectangles' viewer.\n    // The main 'OuterRect', omitted as a field, is window->Rect().\n    ImRect                  OuterRectClipped;                   // == Window->Rect() just after setup in Begin(). == window->Rect() for root window.\n    ImRect                  InnerRect;                          // Inner rectangle (omit title bar, menu bar, scroll bar)\n    ImRect                  InnerClipRect;                      // == InnerRect shrunk by WindowPadding*0.5f on each side, clipped within viewport or parent clip rect.\n    ImRect                  WorkRect;                           // Initially covers the whole scrolling region. Reduced by containers e.g columns/tables when active. Shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentRegionRect over time (from 1.71+ onward).\n    ImRect                  ParentWorkRect;                     // Backup of WorkRect before entering a container such as columns/tables. Used by e.g. SpanAllColumns functions to easily access. Stacked containers are responsible for maintaining this. // FIXME-WORKRECT: Could be a stack?\n    ImRect                  ClipRect;                           // Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList->clip_rect_stack.back().\n    ImRect                  ContentRegionRect;                  // FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on.\n    ImVec2ih                HitTestHoleSize;                    // Define an optional rectangular hole where mouse will pass-through the window.\n    ImVec2ih                HitTestHoleOffset;\n\n    int                     LastFrameActive;                    // Last frame number the window was Active.\n    float                   LastTimeActive;                     // Last timestamp the window was Active (using float as we don't need high precision there)\n    float                   ItemWidthDefault;\n    ImGuiStorage            StateStorage;\n    ImVector<ImGuiOldColumns> ColumnsStorage;\n    float                   FontWindowScale;                    // User scale multiplier per-window, via SetWindowFontScale()\n    int                     SettingsOffset;                     // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back)\n\n    ImDrawList*             DrawList;                           // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer)\n    ImDrawList              DrawListInst;\n    ImGuiWindow*            ParentWindow;                       // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL.\n    ImGuiWindow*            ParentWindowInBeginStack;\n    ImGuiWindow*            RootWindow;                         // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes.\n    ImGuiWindow*            RootWindowPopupTree;                // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child.\n    ImGuiWindow*            RootWindowForTitleBarHighlight;     // Point to ourself or first ancestor which will display TitleBgActive color when this window is active.\n    ImGuiWindow*            RootWindowForNav;                   // Point to ourself or first ancestor which doesn't have the NavFlattened flag.\n\n    ImGuiWindow*            NavLastChildNavWindow;              // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.)\n    ImGuiID                 NavLastIds[ImGuiNavLayer_COUNT];    // Last known NavId for this window, per layer (0/1)\n    ImRect                  NavRectRel[ImGuiNavLayer_COUNT];    // Reference rectangle, in window relative space\n    ImVec2                  NavPreferredScoringPosRel[ImGuiNavLayer_COUNT]; // Preferred X/Y position updated when moving on a given axis, reset to FLT_MAX.\n    ImGuiID                 NavRootFocusScopeId;                // Focus Scope ID at the time of Begin()\n\n    int                     MemoryDrawListIdxCapacity;          // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy\n    int                     MemoryDrawListVtxCapacity;\n    bool                    MemoryCompacted;                    // Set when window extraneous data have been garbage collected\n\npublic:\n    ImGuiWindow(ImGuiContext* context, const char* name);\n    ~ImGuiWindow();\n\n    ImGuiID     GetID(const char* str, const char* str_end = NULL);\n    ImGuiID     GetID(const void* ptr);\n    ImGuiID     GetID(int n);\n    ImGuiID     GetIDFromRectangle(const ImRect& r_abs);\n\n    // We don't use g.FontSize because the window may be != g.CurrentWindow.\n    ImRect      Rect() const            { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); }\n    float       CalcFontSize() const    { ImGuiContext& g = *Ctx; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; }\n    float       TitleBarHeight() const  { ImGuiContext& g = *Ctx; return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + g.Style.FramePadding.y * 2.0f; }\n    ImRect      TitleBarRect() const    { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); }\n    float       MenuBarHeight() const   { ImGuiContext& g = *Ctx; return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + g.Style.FramePadding.y * 2.0f : 0.0f; }\n    ImRect      MenuBarRect() const     { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Tab bar, Tab item support\n//-----------------------------------------------------------------------------\n\n// Extend ImGuiTabBarFlags_\nenum ImGuiTabBarFlagsPrivate_\n{\n    ImGuiTabBarFlags_DockNode                   = 1 << 20,  // Part of a dock node [we don't use this in the master branch but it facilitate branch syncing to keep this around]\n    ImGuiTabBarFlags_IsFocused                  = 1 << 21,\n    ImGuiTabBarFlags_SaveSettings               = 1 << 22,  // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs\n};\n\n// Extend ImGuiTabItemFlags_\nenum ImGuiTabItemFlagsPrivate_\n{\n    ImGuiTabItemFlags_SectionMask_              = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing,\n    ImGuiTabItemFlags_NoCloseButton             = 1 << 20,  // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout)\n    ImGuiTabItemFlags_Button                    = 1 << 21,  // Used by TabItemButton, change the tab item behavior to mimic a button\n};\n\n// Storage for one active tab item (sizeof() 40 bytes)\nstruct ImGuiTabItem\n{\n    ImGuiID             ID;\n    ImGuiTabItemFlags   Flags;\n    int                 LastFrameVisible;\n    int                 LastFrameSelected;      // This allows us to infer an ordered list of the last activated tabs with little maintenance\n    float               Offset;                 // Position relative to beginning of tab\n    float               Width;                  // Width currently displayed\n    float               ContentWidth;           // Width of label, stored during BeginTabItem() call\n    float               RequestedWidth;         // Width optionally requested by caller, -1.0f is unused\n    ImS32               NameOffset;             // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames\n    ImS16               BeginOrder;             // BeginTabItem() order, used to re-order tabs after toggling ImGuiTabBarFlags_Reorderable\n    ImS16               IndexDuringLayout;      // Index only used during TabBarLayout(). Tabs gets reordered so 'Tabs[n].IndexDuringLayout == n' but may mismatch during additions.\n    bool                WantClose;              // Marked as closed by SetTabItemClosed()\n\n    ImGuiTabItem()      { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; }\n};\n\n// Storage for a tab bar (sizeof() 152 bytes)\nstruct IMGUI_API ImGuiTabBar\n{\n    ImVector<ImGuiTabItem> Tabs;\n    ImGuiTabBarFlags    Flags;\n    ImGuiID             ID;                     // Zero for tab-bars used by docking\n    ImGuiID             SelectedTabId;          // Selected tab/window\n    ImGuiID             NextSelectedTabId;      // Next selected tab/window. Will also trigger a scrolling animation\n    ImGuiID             VisibleTabId;           // Can occasionally be != SelectedTabId (e.g. when previewing contents for CTRL+TAB preview)\n    int                 CurrFrameVisible;\n    int                 PrevFrameVisible;\n    ImRect              BarRect;\n    float               CurrTabsContentsHeight;\n    float               PrevTabsContentsHeight; // Record the height of contents submitted below the tab bar\n    float               WidthAllTabs;           // Actual width of all tabs (locked during layout)\n    float               WidthAllTabsIdeal;      // Ideal width if all tabs were visible and not clipped\n    float               ScrollingAnim;\n    float               ScrollingTarget;\n    float               ScrollingTargetDistToVisibility;\n    float               ScrollingSpeed;\n    float               ScrollingRectMinX;\n    float               ScrollingRectMaxX;\n    float               SeparatorMinX;\n    float               SeparatorMaxX;\n    ImGuiID             ReorderRequestTabId;\n    ImS16               ReorderRequestOffset;\n    ImS8                BeginCount;\n    bool                WantLayout;\n    bool                VisibleTabWasSubmitted;\n    bool                TabsAddedNew;           // Set to true when a new tab item or button has been added to the tab bar during last frame\n    ImS16               TabsActiveCount;        // Number of tabs submitted this frame.\n    ImS16               LastTabItemIdx;         // Index of last BeginTabItem() tab for use by EndTabItem()\n    float               ItemSpacingY;\n    ImVec2              FramePadding;           // style.FramePadding locked at the time of BeginTabBar()\n    ImVec2              BackupCursorPos;\n    ImGuiTextBuffer     TabsNames;              // For non-docking tab bar we re-append names in a contiguous buffer.\n\n    ImGuiTabBar();\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] Table support\n//-----------------------------------------------------------------------------\n\n#define IM_COL32_DISABLE                IM_COL32(0,0,0,1)   // Special sentinel code which cannot be used as a regular color.\n#define IMGUI_TABLE_MAX_COLUMNS         512                 // May be further lifted\n\n// Our current column maximum is 64 but we may raise that in the future.\ntypedef ImS16 ImGuiTableColumnIdx;\ntypedef ImU16 ImGuiTableDrawChannelIdx;\n\n// [Internal] sizeof() ~ 112\n// We use the terminology \"Enabled\" to refer to a column that is not Hidden by user/api.\n// We use the terminology \"Clipped\" to refer to a column that is out of sight because of scrolling/clipping.\n// This is in contrast with some user-facing api such as IsItemVisible() / IsRectVisible() which use \"Visible\" to mean \"not clipped\".\nstruct ImGuiTableColumn\n{\n    ImGuiTableColumnFlags   Flags;                          // Flags after some patching (not directly same as provided by user). See ImGuiTableColumnFlags_\n    float                   WidthGiven;                     // Final/actual width visible == (MaxX - MinX), locked in TableUpdateLayout(). May be > WidthRequest to honor minimum width, may be < WidthRequest to honor shrinking columns down in tight space.\n    float                   MinX;                           // Absolute positions\n    float                   MaxX;\n    float                   WidthRequest;                   // Master width absolute value when !(Flags & _WidthStretch). When Stretch this is derived every frame from StretchWeight in TableUpdateLayout()\n    float                   WidthAuto;                      // Automatic width\n    float                   StretchWeight;                  // Master width weight when (Flags & _WidthStretch). Often around ~1.0f initially.\n    float                   InitStretchWeightOrWidth;       // Value passed to TableSetupColumn(). For Width it is a content width (_without padding_).\n    ImRect                  ClipRect;                       // Clipping rectangle for the column\n    ImGuiID                 UserID;                         // Optional, value passed to TableSetupColumn()\n    float                   WorkMinX;                       // Contents region min ~(MinX + CellPaddingX + CellSpacingX1) == cursor start position when entering column\n    float                   WorkMaxX;                       // Contents region max ~(MaxX - CellPaddingX - CellSpacingX2)\n    float                   ItemWidth;                      // Current item width for the column, preserved across rows\n    float                   ContentMaxXFrozen;              // Contents maximum position for frozen rows (apart from headers), from which we can infer content width.\n    float                   ContentMaxXUnfrozen;\n    float                   ContentMaxXHeadersUsed;         // Contents maximum position for headers rows (regardless of freezing). TableHeader() automatically softclip itself + report ideal desired size, to avoid creating extraneous draw calls\n    float                   ContentMaxXHeadersIdeal;\n    ImS16                   NameOffset;                     // Offset into parent ColumnsNames[]\n    ImGuiTableColumnIdx     DisplayOrder;                   // Index within Table's IndexToDisplayOrder[] (column may be reordered by users)\n    ImGuiTableColumnIdx     IndexWithinEnabledSet;          // Index within enabled/visible set (<= IndexToDisplayOrder)\n    ImGuiTableColumnIdx     PrevEnabledColumn;              // Index of prev enabled/visible column within Columns[], -1 if first enabled/visible column\n    ImGuiTableColumnIdx     NextEnabledColumn;              // Index of next enabled/visible column within Columns[], -1 if last enabled/visible column\n    ImGuiTableColumnIdx     SortOrder;                      // Index of this column within sort specs, -1 if not sorting on this column, 0 for single-sort, may be >0 on multi-sort\n    ImGuiTableDrawChannelIdx DrawChannelCurrent;            // Index within DrawSplitter.Channels[]\n    ImGuiTableDrawChannelIdx DrawChannelFrozen;             // Draw channels for frozen rows (often headers)\n    ImGuiTableDrawChannelIdx DrawChannelUnfrozen;           // Draw channels for unfrozen rows\n    bool                    IsEnabled;                      // IsUserEnabled && (Flags & ImGuiTableColumnFlags_Disabled) == 0\n    bool                    IsUserEnabled;                  // Is the column not marked Hidden by the user? (unrelated to being off view, e.g. clipped by scrolling).\n    bool                    IsUserEnabledNextFrame;\n    bool                    IsVisibleX;                     // Is actually in view (e.g. overlapping the host window clipping rectangle, not scrolled).\n    bool                    IsVisibleY;\n    bool                    IsRequestOutput;                // Return value for TableSetColumnIndex() / TableNextColumn(): whether we request user to output contents or not.\n    bool                    IsSkipItems;                    // Do we want item submissions to this column to be completely ignored (no layout will happen).\n    bool                    IsPreserveWidthAuto;\n    ImS8                    NavLayerCurrent;                // ImGuiNavLayer in 1 byte\n    ImU8                    AutoFitQueue;                   // Queue of 8 values for the next 8 frames to request auto-fit\n    ImU8                    CannotSkipItemsQueue;           // Queue of 8 values for the next 8 frames to disable Clipped/SkipItem\n    ImU8                    SortDirection : 2;              // ImGuiSortDirection_Ascending or ImGuiSortDirection_Descending\n    ImU8                    SortDirectionsAvailCount : 2;   // Number of available sort directions (0 to 3)\n    ImU8                    SortDirectionsAvailMask : 4;    // Mask of available sort directions (1-bit each)\n    ImU8                    SortDirectionsAvailList;        // Ordered list of available sort directions (2-bits each, total 8-bits)\n\n    ImGuiTableColumn()\n    {\n        memset(this, 0, sizeof(*this));\n        StretchWeight = WidthRequest = -1.0f;\n        NameOffset = -1;\n        DisplayOrder = IndexWithinEnabledSet = -1;\n        PrevEnabledColumn = NextEnabledColumn = -1;\n        SortOrder = -1;\n        SortDirection = ImGuiSortDirection_None;\n        DrawChannelCurrent = DrawChannelFrozen = DrawChannelUnfrozen = (ImU8)-1;\n    }\n};\n\n// Transient cell data stored per row.\n// sizeof() ~ 6\nstruct ImGuiTableCellData\n{\n    ImU32                       BgColor;    // Actual color\n    ImGuiTableColumnIdx         Column;     // Column number\n};\n\n// Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs. Does that means they could be moved to ImGuiTableTempData?)\n// sizeof() ~ 24 bytes\nstruct ImGuiTableInstanceData\n{\n    ImGuiID                     TableInstanceID;\n    float                       LastOuterHeight;            // Outer height from last frame\n    float                       LastTopHeadersRowHeight;    // Height of first consecutive header rows from last frame (FIXME: this is used assuming consecutive headers are in same frozen set)\n    float                       LastFrozenHeight;           // Height of frozen section from last frame\n    int                         HoveredRowLast;             // Index of row which was hovered last frame.\n    int                         HoveredRowNext;             // Index of row hovered this frame, set after encountering it.\n\n    ImGuiTableInstanceData()    { TableInstanceID = 0; LastOuterHeight = LastTopHeadersRowHeight = LastFrozenHeight = 0.0f; HoveredRowLast = HoveredRowNext = -1; }\n};\n\n// FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs, incoming RowData\n// sizeof() ~ 580 bytes + heap allocs described in TableBeginInitMemory()\nstruct IMGUI_API ImGuiTable\n{\n    ImGuiID                     ID;\n    ImGuiTableFlags             Flags;\n    void*                       RawData;                    // Single allocation to hold Columns[], DisplayOrderToIndex[] and RowCellData[]\n    ImGuiTableTempData*         TempData;                   // Transient data while table is active. Point within g.CurrentTableStack[]\n    ImSpan<ImGuiTableColumn>    Columns;                    // Point within RawData[]\n    ImSpan<ImGuiTableColumnIdx> DisplayOrderToIndex;        // Point within RawData[]. Store display order of columns (when not reordered, the values are 0...Count-1)\n    ImSpan<ImGuiTableCellData>  RowCellData;                // Point within RawData[]. Store cells background requests for current row.\n    ImBitArrayPtr               EnabledMaskByDisplayOrder;  // Column DisplayOrder -> IsEnabled map\n    ImBitArrayPtr               EnabledMaskByIndex;         // Column Index -> IsEnabled map (== not hidden by user/api) in a format adequate for iterating column without touching cold data\n    ImBitArrayPtr               VisibleMaskByIndex;         // Column Index -> IsVisibleX|IsVisibleY map (== not hidden by user/api && not hidden by scrolling/cliprect)\n    ImGuiTableFlags             SettingsLoadedFlags;        // Which data were loaded from the .ini file (e.g. when order is not altered we won't save order)\n    int                         SettingsOffset;             // Offset in g.SettingsTables\n    int                         LastFrameActive;\n    int                         ColumnsCount;               // Number of columns declared in BeginTable()\n    int                         CurrentRow;\n    int                         CurrentColumn;\n    ImS16                       InstanceCurrent;            // Count of BeginTable() calls with same ID in the same frame (generally 0). This is a little bit similar to BeginCount for a window, but multiple table with same ID look are multiple tables, they are just synched.\n    ImS16                       InstanceInteracted;         // Mark which instance (generally 0) of the same ID is being interacted with\n    float                       RowPosY1;\n    float                       RowPosY2;\n    float                       RowMinHeight;               // Height submitted to TableNextRow()\n    float                       RowCellPaddingY;            // Top and bottom padding. Reloaded during row change.\n    float                       RowTextBaseline;\n    float                       RowIndentOffsetX;\n    ImGuiTableRowFlags          RowFlags : 16;              // Current row flags, see ImGuiTableRowFlags_\n    ImGuiTableRowFlags          LastRowFlags : 16;\n    int                         RowBgColorCounter;          // Counter for alternating background colors (can be fast-forwarded by e.g clipper), not same as CurrentRow because header rows typically don't increase this.\n    ImU32                       RowBgColor[2];              // Background color override for current row.\n    ImU32                       BorderColorStrong;\n    ImU32                       BorderColorLight;\n    float                       BorderX1;\n    float                       BorderX2;\n    float                       HostIndentX;\n    float                       MinColumnWidth;\n    float                       OuterPaddingX;\n    float                       CellPaddingX;               // Padding from each borders. Locked in BeginTable()/Layout.\n    float                       CellSpacingX1;              // Spacing between non-bordered cells. Locked in BeginTable()/Layout.\n    float                       CellSpacingX2;\n    float                       InnerWidth;                 // User value passed to BeginTable(), see comments at the top of BeginTable() for details.\n    float                       ColumnsGivenWidth;          // Sum of current column width\n    float                       ColumnsAutoFitWidth;        // Sum of ideal column width in order nothing to be clipped, used for auto-fitting and content width submission in outer window\n    float                       ColumnsStretchSumWeights;   // Sum of weight of all enabled stretching columns\n    float                       ResizedColumnNextWidth;\n    float                       ResizeLockMinContentsX2;    // Lock minimum contents width while resizing down in order to not create feedback loops. But we allow growing the table.\n    float                       RefScale;                   // Reference scale to be able to rescale columns on font/dpi changes.\n    float                       AngledHeadersHeight;        // Set by TableAngledHeadersRow(), used in TableUpdateLayout()\n    float                       AngledHeadersSlope;         // Set by TableAngledHeadersRow(), used in TableUpdateLayout()\n    ImRect                      OuterRect;                  // Note: for non-scrolling table, OuterRect.Max.y is often FLT_MAX until EndTable(), unless a height has been specified in BeginTable().\n    ImRect                      InnerRect;                  // InnerRect but without decoration. As with OuterRect, for non-scrolling tables, InnerRect.Max.y is\n    ImRect                      WorkRect;\n    ImRect                      InnerClipRect;\n    ImRect                      BgClipRect;                 // We use this to cpu-clip cell background color fill, evolve during the frame as we cross frozen rows boundaries\n    ImRect                      Bg0ClipRectForDrawCmd;      // Actual ImDrawCmd clip rect for BG0/1 channel. This tends to be == OuterWindow->ClipRect at BeginTable() because output in BG0/BG1 is cpu-clipped\n    ImRect                      Bg2ClipRectForDrawCmd;      // Actual ImDrawCmd clip rect for BG2 channel. This tends to be a correct, tight-fit, because output to BG2 are done by widgets relying on regular ClipRect.\n    ImRect                      HostClipRect;               // This is used to check if we can eventually merge our columns draw calls into the current draw call of the current window.\n    ImRect                      HostBackupInnerClipRect;    // Backup of InnerWindow->ClipRect during PushTableBackground()/PopTableBackground()\n    ImGuiWindow*                OuterWindow;                // Parent window for the table\n    ImGuiWindow*                InnerWindow;                // Window holding the table data (== OuterWindow or a child window)\n    ImGuiTextBuffer             ColumnsNames;               // Contiguous buffer holding columns names\n    ImDrawListSplitter*         DrawSplitter;               // Shortcut to TempData->DrawSplitter while in table. Isolate draw commands per columns to avoid switching clip rect constantly\n    ImGuiTableInstanceData      InstanceDataFirst;\n    ImVector<ImGuiTableInstanceData>    InstanceDataExtra;  // FIXME-OPT: Using a small-vector pattern would be good.\n    ImGuiTableColumnSortSpecs   SortSpecsSingle;\n    ImVector<ImGuiTableColumnSortSpecs> SortSpecsMulti;     // FIXME-OPT: Using a small-vector pattern would be good.\n    ImGuiTableSortSpecs         SortSpecs;                  // Public facing sorts specs, this is what we return in TableGetSortSpecs()\n    ImGuiTableColumnIdx         SortSpecsCount;\n    ImGuiTableColumnIdx         ColumnsEnabledCount;        // Number of enabled columns (<= ColumnsCount)\n    ImGuiTableColumnIdx         ColumnsEnabledFixedCount;   // Number of enabled columns (<= ColumnsCount)\n    ImGuiTableColumnIdx         DeclColumnsCount;           // Count calls to TableSetupColumn()\n    ImGuiTableColumnIdx         AngledHeadersCount;         // Count columns with angled headers\n    ImGuiTableColumnIdx         HoveredColumnBody;          // Index of column whose visible region is being hovered. Important: == ColumnsCount when hovering empty region after the right-most column!\n    ImGuiTableColumnIdx         HoveredColumnBorder;        // Index of column whose right-border is being hovered (for resizing).\n    ImGuiTableColumnIdx         HighlightColumnHeader;      // Index of column which should be highlighted.\n    ImGuiTableColumnIdx         AutoFitSingleColumn;        // Index of single column requesting auto-fit.\n    ImGuiTableColumnIdx         ResizedColumn;              // Index of column being resized. Reset when InstanceCurrent==0.\n    ImGuiTableColumnIdx         LastResizedColumn;          // Index of column being resized from previous frame.\n    ImGuiTableColumnIdx         HeldHeaderColumn;           // Index of column header being held.\n    ImGuiTableColumnIdx         ReorderColumn;              // Index of column being reordered. (not cleared)\n    ImGuiTableColumnIdx         ReorderColumnDir;           // -1 or +1\n    ImGuiTableColumnIdx         LeftMostEnabledColumn;      // Index of left-most non-hidden column.\n    ImGuiTableColumnIdx         RightMostEnabledColumn;     // Index of right-most non-hidden column.\n    ImGuiTableColumnIdx         LeftMostStretchedColumn;    // Index of left-most stretched column.\n    ImGuiTableColumnIdx         RightMostStretchedColumn;   // Index of right-most stretched column.\n    ImGuiTableColumnIdx         ContextPopupColumn;         // Column right-clicked on, of -1 if opening context menu from a neutral/empty spot\n    ImGuiTableColumnIdx         FreezeRowsRequest;          // Requested frozen rows count\n    ImGuiTableColumnIdx         FreezeRowsCount;            // Actual frozen row count (== FreezeRowsRequest, or == 0 when no scrolling offset)\n    ImGuiTableColumnIdx         FreezeColumnsRequest;       // Requested frozen columns count\n    ImGuiTableColumnIdx         FreezeColumnsCount;         // Actual frozen columns count (== FreezeColumnsRequest, or == 0 when no scrolling offset)\n    ImGuiTableColumnIdx         RowCellDataCurrent;         // Index of current RowCellData[] entry in current row\n    ImGuiTableDrawChannelIdx    DummyDrawChannel;           // Redirect non-visible columns here.\n    ImGuiTableDrawChannelIdx    Bg2DrawChannelCurrent;      // For Selectable() and other widgets drawing across columns after the freezing line. Index within DrawSplitter.Channels[]\n    ImGuiTableDrawChannelIdx    Bg2DrawChannelUnfrozen;\n    bool                        IsLayoutLocked;             // Set by TableUpdateLayout() which is called when beginning the first row.\n    bool                        IsInsideRow;                // Set when inside TableBeginRow()/TableEndRow().\n    bool                        IsInitializing;\n    bool                        IsSortSpecsDirty;\n    bool                        IsUsingHeaders;             // Set when the first row had the ImGuiTableRowFlags_Headers flag.\n    bool                        IsContextPopupOpen;         // Set when default context menu is open (also see: ContextPopupColumn, InstanceInteracted).\n    bool                        DisableDefaultContextMenu;  // Disable default context menu contents. You may submit your own using TableBeginContextMenuPopup()/EndPopup()\n    bool                        IsSettingsRequestLoad;\n    bool                        IsSettingsDirty;            // Set when table settings have changed and needs to be reported into ImGuiTableSetttings data.\n    bool                        IsDefaultDisplayOrder;      // Set when display order is unchanged from default (DisplayOrder contains 0...Count-1)\n    bool                        IsResetAllRequest;\n    bool                        IsResetDisplayOrderRequest;\n    bool                        IsUnfrozenRows;             // Set when we got past the frozen row.\n    bool                        IsDefaultSizingPolicy;      // Set if user didn't explicitly set a sizing policy in BeginTable()\n    bool                        IsActiveIdAliveBeforeTable;\n    bool                        IsActiveIdInTable;\n    bool                        HasScrollbarYCurr;          // Whether ANY instance of this table had a vertical scrollbar during the current frame.\n    bool                        HasScrollbarYPrev;          // Whether ANY instance of this table had a vertical scrollbar during the previous.\n    bool                        MemoryCompacted;\n    bool                        HostSkipItems;              // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis\n\n    ImGuiTable()                { memset(this, 0, sizeof(*this)); LastFrameActive = -1; }\n    ~ImGuiTable()               { IM_FREE(RawData); }\n};\n\n// Transient data that are only needed between BeginTable() and EndTable(), those buffers are shared (1 per level of stacked table).\n// - Accessing those requires chasing an extra pointer so for very frequently used data we leave them in the main table structure.\n// - We also leave out of this structure data that tend to be particularly useful for debugging/metrics.\n// sizeof() ~ 120 bytes.\nstruct IMGUI_API ImGuiTableTempData\n{\n    int                         TableIndex;                 // Index in g.Tables.Buf[] pool\n    float                       LastTimeActive;             // Last timestamp this structure was used\n    float                       AngledheadersExtraWidth;    // Used in EndTable()\n\n    ImVec2                      UserOuterSize;              // outer_size.x passed to BeginTable()\n    ImDrawListSplitter          DrawSplitter;\n\n    ImRect                      HostBackupWorkRect;         // Backup of InnerWindow->WorkRect at the end of BeginTable()\n    ImRect                      HostBackupParentWorkRect;   // Backup of InnerWindow->ParentWorkRect at the end of BeginTable()\n    ImVec2                      HostBackupPrevLineSize;     // Backup of InnerWindow->DC.PrevLineSize at the end of BeginTable()\n    ImVec2                      HostBackupCurrLineSize;     // Backup of InnerWindow->DC.CurrLineSize at the end of BeginTable()\n    ImVec2                      HostBackupCursorMaxPos;     // Backup of InnerWindow->DC.CursorMaxPos at the end of BeginTable()\n    ImVec1                      HostBackupColumnsOffset;    // Backup of OuterWindow->DC.ColumnsOffset at the end of BeginTable()\n    float                       HostBackupItemWidth;        // Backup of OuterWindow->DC.ItemWidth at the end of BeginTable()\n    int                         HostBackupItemWidthStackSize;//Backup of OuterWindow->DC.ItemWidthStack.Size at the end of BeginTable()\n\n    ImGuiTableTempData()        { memset(this, 0, sizeof(*this)); LastTimeActive = -1.0f; }\n};\n\n// sizeof() ~ 12\nstruct ImGuiTableColumnSettings\n{\n    float                   WidthOrWeight;\n    ImGuiID                 UserID;\n    ImGuiTableColumnIdx     Index;\n    ImGuiTableColumnIdx     DisplayOrder;\n    ImGuiTableColumnIdx     SortOrder;\n    ImU8                    SortDirection : 2;\n    ImU8                    IsEnabled : 1; // \"Visible\" in ini file\n    ImU8                    IsStretch : 1;\n\n    ImGuiTableColumnSettings()\n    {\n        WidthOrWeight = 0.0f;\n        UserID = 0;\n        Index = -1;\n        DisplayOrder = SortOrder = -1;\n        SortDirection = ImGuiSortDirection_None;\n        IsEnabled = 1;\n        IsStretch = 0;\n    }\n};\n\n// This is designed to be stored in a single ImChunkStream (1 header followed by N ImGuiTableColumnSettings, etc.)\nstruct ImGuiTableSettings\n{\n    ImGuiID                     ID;                     // Set to 0 to invalidate/delete the setting\n    ImGuiTableFlags             SaveFlags;              // Indicate data we want to save using the Resizable/Reorderable/Sortable/Hideable flags (could be using its own flags..)\n    float                       RefScale;               // Reference scale to be able to rescale columns on font/dpi changes.\n    ImGuiTableColumnIdx         ColumnsCount;\n    ImGuiTableColumnIdx         ColumnsCountMax;        // Maximum number of columns this settings instance can store, we can recycle a settings instance with lower number of columns but not higher\n    bool                        WantApply;              // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context)\n\n    ImGuiTableSettings()        { memset(this, 0, sizeof(*this)); }\n    ImGuiTableColumnSettings*   GetColumnSettings()     { return (ImGuiTableColumnSettings*)(this + 1); }\n};\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImGui internal API\n// No guarantee of forward compatibility here!\n//-----------------------------------------------------------------------------\n\nnamespace ImGui\n{\n    // Windows\n    // We should always have a CurrentWindow in the stack (there is an implicit \"Debug\" window)\n    // If this ever crash because g.CurrentWindow is NULL it means that either\n    // - ImGui::NewFrame() has never been called, which is illegal.\n    // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal.\n    inline    ImGuiWindow*  GetCurrentWindowRead()      { ImGuiContext& g = *GImGui; return g.CurrentWindow; }\n    inline    ImGuiWindow*  GetCurrentWindow()          { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; }\n    IMGUI_API ImGuiWindow*  FindWindowByID(ImGuiID id);\n    IMGUI_API ImGuiWindow*  FindWindowByName(const char* name);\n    IMGUI_API void          UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);\n    IMGUI_API ImVec2        CalcWindowNextAutoFitSize(ImGuiWindow* window);\n    IMGUI_API bool          IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy);\n    IMGUI_API bool          IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent);\n    IMGUI_API bool          IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below);\n    IMGUI_API bool          IsWindowNavFocusable(ImGuiWindow* window);\n    IMGUI_API void          SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0);\n    IMGUI_API void          SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond = 0);\n    IMGUI_API void          SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0);\n    IMGUI_API void          SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size);\n    IMGUI_API void          SetWindowHiddendAndSkipItemsForCurrentFrame(ImGuiWindow* window);\n    inline ImRect           WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); }\n    inline ImRect           WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); }\n    inline ImVec2           WindowPosRelToAbs(ImGuiWindow* window, const ImVec2& p)  { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x + off.x, p.y + off.y); }\n\n    // Windows: Display Order and Focus Order\n    IMGUI_API void          FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags = 0);\n    IMGUI_API void          FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags);\n    IMGUI_API void          BringWindowToFocusFront(ImGuiWindow* window);\n    IMGUI_API void          BringWindowToDisplayFront(ImGuiWindow* window);\n    IMGUI_API void          BringWindowToDisplayBack(ImGuiWindow* window);\n    IMGUI_API void          BringWindowToDisplayBehind(ImGuiWindow* window, ImGuiWindow* above_window);\n    IMGUI_API int           FindWindowDisplayIndex(ImGuiWindow* window);\n    IMGUI_API ImGuiWindow*  FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* window);\n\n    // Fonts, drawing\n    IMGUI_API void          SetCurrentFont(ImFont* font);\n    inline ImFont*          GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; }\n    inline ImDrawList*      GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches.\n    IMGUI_API ImDrawList*   GetBackgroundDrawList(ImGuiViewport* viewport);                     // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents.\n    IMGUI_API ImDrawList*   GetForegroundDrawList(ImGuiViewport* viewport);                     // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents.\n    IMGUI_API void          AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector<ImDrawList*>* out_list, ImDrawList* draw_list);\n\n    // Init\n    IMGUI_API void          Initialize();\n    IMGUI_API void          Shutdown();    // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext().\n\n    // NewFrame\n    IMGUI_API void          UpdateInputEvents(bool trickle_fast_inputs);\n    IMGUI_API void          UpdateHoveredWindowAndCaptureFlags();\n    IMGUI_API void          StartMouseMovingWindow(ImGuiWindow* window);\n    IMGUI_API void          UpdateMouseMovingWindowNewFrame();\n    IMGUI_API void          UpdateMouseMovingWindowEndFrame();\n\n    // Generic context hooks\n    IMGUI_API ImGuiID       AddContextHook(ImGuiContext* context, const ImGuiContextHook* hook);\n    IMGUI_API void          RemoveContextHook(ImGuiContext* context, ImGuiID hook_to_remove);\n    IMGUI_API void          CallContextHooks(ImGuiContext* context, ImGuiContextHookType type);\n\n    // Viewports\n    IMGUI_API void          SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport);\n\n    // Settings\n    IMGUI_API void                  MarkIniSettingsDirty();\n    IMGUI_API void                  MarkIniSettingsDirty(ImGuiWindow* window);\n    IMGUI_API void                  ClearIniSettings();\n    IMGUI_API void                  AddSettingsHandler(const ImGuiSettingsHandler* handler);\n    IMGUI_API void                  RemoveSettingsHandler(const char* type_name);\n    IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name);\n\n    // Settings - Windows\n    IMGUI_API ImGuiWindowSettings*  CreateNewWindowSettings(const char* name);\n    IMGUI_API ImGuiWindowSettings*  FindWindowSettingsByID(ImGuiID id);\n    IMGUI_API ImGuiWindowSettings*  FindWindowSettingsByWindow(ImGuiWindow* window);\n    IMGUI_API void                  ClearWindowSettings(const char* name);\n\n    // Localization\n    IMGUI_API void          LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count);\n    inline const char*      LocalizeGetMsg(ImGuiLocKey key) { ImGuiContext& g = *GImGui; const char* msg = g.LocalizationTable[key]; return msg ? msg : \"*Missing Text*\"; }\n\n    // Scrolling\n    IMGUI_API void          SetScrollX(ImGuiWindow* window, float scroll_x);\n    IMGUI_API void          SetScrollY(ImGuiWindow* window, float scroll_y);\n    IMGUI_API void          SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio);\n    IMGUI_API void          SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio);\n\n    // Early work-in-progress API (ScrollToItem() will become public)\n    IMGUI_API void          ScrollToItem(ImGuiScrollFlags flags = 0);\n    IMGUI_API void          ScrollToRect(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0);\n    IMGUI_API ImVec2        ScrollToRectEx(ImGuiWindow* window, const ImRect& rect, ImGuiScrollFlags flags = 0);\n//#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    inline void             ScrollToBringRectIntoView(ImGuiWindow* window, const ImRect& rect) { ScrollToRect(window, rect, ImGuiScrollFlags_KeepVisibleEdgeY); }\n//#endif\n\n    // Basic Accessors\n    inline ImGuiItemStatusFlags GetItemStatusFlags(){ ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; }\n    inline ImGuiItemFlags   GetItemFlags()  { ImGuiContext& g = *GImGui; return g.LastItemData.InFlags; }\n    inline ImGuiID          GetActiveID()   { ImGuiContext& g = *GImGui; return g.ActiveId; }\n    inline ImGuiID          GetFocusID()    { ImGuiContext& g = *GImGui; return g.NavId; }\n    IMGUI_API void          SetActiveID(ImGuiID id, ImGuiWindow* window);\n    IMGUI_API void          SetFocusID(ImGuiID id, ImGuiWindow* window);\n    IMGUI_API void          ClearActiveID();\n    IMGUI_API ImGuiID       GetHoveredID();\n    IMGUI_API void          SetHoveredID(ImGuiID id);\n    IMGUI_API void          KeepAliveID(ImGuiID id);\n    IMGUI_API void          MarkItemEdited(ImGuiID id);     // Mark data associated to given item as \"edited\", used by IsItemDeactivatedAfterEdit() function.\n    IMGUI_API void          PushOverrideID(ImGuiID id);     // Push given value as-is at the top of the ID stack (whereas PushID combines old and new hashes)\n    IMGUI_API ImGuiID       GetIDWithSeed(const char* str_id_begin, const char* str_id_end, ImGuiID seed);\n    IMGUI_API ImGuiID       GetIDWithSeed(int n, ImGuiID seed);\n\n    // Basic Helpers for widget code\n    IMGUI_API void          ItemSize(const ImVec2& size, float text_baseline_y = -1.0f);\n    inline void             ItemSize(const ImRect& bb, float text_baseline_y = -1.0f) { ItemSize(bb.GetSize(), text_baseline_y); } // FIXME: This is a misleading API since we expect CursorPos to be bb.Min.\n    IMGUI_API bool          ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL, ImGuiItemFlags extra_flags = 0);\n    IMGUI_API bool          ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags);\n    IMGUI_API bool          IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0);\n    IMGUI_API bool          IsClippedEx(const ImRect& bb, ImGuiID id);\n    IMGUI_API void          SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect);\n    IMGUI_API ImVec2        CalcItemSize(ImVec2 size, float default_w, float default_h);\n    IMGUI_API float         CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x);\n    IMGUI_API void          PushMultiItemsWidths(int components, float width_full);\n    IMGUI_API bool          IsItemToggledSelection();                                   // Was the last item selection toggled? (after Selectable(), TreeNode() etc. We only returns toggle _event_ in order to handle clipping correctly)\n    IMGUI_API ImVec2        GetContentRegionMaxAbs();\n    IMGUI_API void          ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess);\n\n    // Parameter stacks (shared)\n    IMGUI_API void          PushItemFlag(ImGuiItemFlags option, bool enabled);\n    IMGUI_API void          PopItemFlag();\n    IMGUI_API const ImGuiDataVarInfo* GetStyleVarInfo(ImGuiStyleVar idx);\n\n    // Logging/Capture\n    IMGUI_API void          LogBegin(ImGuiLogType type, int auto_open_depth);           // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name.\n    IMGUI_API void          LogToBuffer(int auto_open_depth = -1);                      // Start logging/capturing to internal buffer\n    IMGUI_API void          LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL);\n    IMGUI_API void          LogSetNextTextDecoration(const char* prefix, const char* suffix);\n\n    // Popups, Modals, Tooltips\n    IMGUI_API bool          BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags);\n    IMGUI_API void          OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_None);\n    IMGUI_API void          ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup);\n    IMGUI_API void          ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to_window_under_popup);\n    IMGUI_API void          ClosePopupsExceptModals();\n    IMGUI_API bool          IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags);\n    IMGUI_API bool          BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags);\n    IMGUI_API bool          BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags);\n    IMGUI_API bool          BeginTooltipHidden();\n    IMGUI_API ImRect        GetPopupAllowedExtentRect(ImGuiWindow* window);\n    IMGUI_API ImGuiWindow*  GetTopMostPopupModal();\n    IMGUI_API ImGuiWindow*  GetTopMostAndVisiblePopupModal();\n    IMGUI_API ImGuiWindow*  FindBlockingModal(ImGuiWindow* window);\n    IMGUI_API ImVec2        FindBestWindowPosForPopup(ImGuiWindow* window);\n    IMGUI_API ImVec2        FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy);\n\n    // Menus\n    IMGUI_API bool          BeginViewportSideBar(const char* name, ImGuiViewport* viewport, ImGuiDir dir, float size, ImGuiWindowFlags window_flags);\n    IMGUI_API bool          BeginMenuEx(const char* label, const char* icon, bool enabled = true);\n    IMGUI_API bool          MenuItemEx(const char* label, const char* icon, const char* shortcut = NULL, bool selected = false, bool enabled = true);\n\n    // Combos\n    IMGUI_API bool          BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags);\n    IMGUI_API bool          BeginComboPreview();\n    IMGUI_API void          EndComboPreview();\n\n    // Gamepad/Keyboard Navigation\n    IMGUI_API void          NavInitWindow(ImGuiWindow* window, bool force_reinit);\n    IMGUI_API void          NavInitRequestApplyResult();\n    IMGUI_API bool          NavMoveRequestButNoResultYet();\n    IMGUI_API void          NavMoveRequestSubmit(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);\n    IMGUI_API void          NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, ImGuiNavMoveFlags move_flags, ImGuiScrollFlags scroll_flags);\n    IMGUI_API void          NavMoveRequestResolveWithLastItem(ImGuiNavItemData* result);\n    IMGUI_API void          NavMoveRequestResolveWithPastTreeNode(ImGuiNavItemData* result, ImGuiNavTreeNodeData* tree_node_data);\n    IMGUI_API void          NavMoveRequestCancel();\n    IMGUI_API void          NavMoveRequestApplyResult();\n    IMGUI_API void          NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags);\n    IMGUI_API void          NavClearPreferredPosForAxis(ImGuiAxis axis);\n    IMGUI_API void          NavRestoreHighlightAfterMove();\n    IMGUI_API void          NavUpdateCurrentWindowIsScrollPushableX();\n    IMGUI_API void          SetNavWindow(ImGuiWindow* window);\n    IMGUI_API void          SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel);\n\n    // Focus/Activation\n    // This should be part of a larger set of API: FocusItem(offset = -1), FocusItemByID(id), ActivateItem(offset = -1), ActivateItemByID(id) etc. which are\n    // much harder to design and implement than expected. I have a couple of private branches on this matter but it's not simple. For now implementing the easy ones.\n    IMGUI_API void          FocusItem();                    // Focus last item (no selection/activation).\n    IMGUI_API void          ActivateItemByID(ImGuiID id);   // Activate an item by ID (button, checkbox, tree node etc.). Activation is queued and processed on the next frame when the item is encountered again.\n\n    // Inputs\n    // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions.\n    inline bool             IsNamedKey(ImGuiKey key)                                    { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; }\n    inline bool             IsNamedKeyOrModKey(ImGuiKey key)                            { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super || key == ImGuiMod_Shortcut; }\n    inline bool             IsLegacyKey(ImGuiKey key)                                   { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; }\n    inline bool             IsKeyboardKey(ImGuiKey key)                                 { return key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END; }\n    inline bool             IsGamepadKey(ImGuiKey key)                                  { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; }\n    inline bool             IsMouseKey(ImGuiKey key)                                    { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; }\n    inline bool             IsAliasKey(ImGuiKey key)                                    { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; }\n    inline ImGuiKeyChord    ConvertShortcutMod(ImGuiKeyChord key_chord)                 { ImGuiContext& g = *GImGui; IM_ASSERT_PARANOID(key_chord & ImGuiMod_Shortcut); return (key_chord & ~ImGuiMod_Shortcut) | (g.IO.ConfigMacOSXBehaviors ? ImGuiMod_Super : ImGuiMod_Ctrl); }\n    inline ImGuiKey         ConvertSingleModFlagToKey(ImGuiContext* ctx, ImGuiKey key)\n    {\n        ImGuiContext& g = *ctx;\n        if (key == ImGuiMod_Ctrl) return ImGuiKey_ReservedForModCtrl;\n        if (key == ImGuiMod_Shift) return ImGuiKey_ReservedForModShift;\n        if (key == ImGuiMod_Alt) return ImGuiKey_ReservedForModAlt;\n        if (key == ImGuiMod_Super) return ImGuiKey_ReservedForModSuper;\n        if (key == ImGuiMod_Shortcut) return (g.IO.ConfigMacOSXBehaviors ? ImGuiKey_ReservedForModSuper : ImGuiKey_ReservedForModCtrl);\n        return key;\n    }\n\n    IMGUI_API ImGuiKeyData* GetKeyData(ImGuiContext* ctx, ImGuiKey key);\n    inline ImGuiKeyData*    GetKeyData(ImGuiKey key)                                    { ImGuiContext& g = *GImGui; return GetKeyData(&g, key); }\n    IMGUI_API void          GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size);\n    inline ImGuiKey         MouseButtonToKey(ImGuiMouseButton button)                   { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return (ImGuiKey)(ImGuiKey_MouseLeft + button); }\n    IMGUI_API bool          IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f);\n    IMGUI_API ImVec2        GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down);\n    IMGUI_API float         GetNavTweakPressedAmount(ImGuiAxis axis);\n    IMGUI_API int           CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate);\n    IMGUI_API void          GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate);\n    IMGUI_API void          TeleportMousePos(const ImVec2& pos);\n    IMGUI_API void          SetActiveIdUsingAllKeyboardKeys();\n    inline bool             IsActiveIdUsingNavDir(ImGuiDir dir)                         { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; }\n\n    // [EXPERIMENTAL] Low-Level: Key/Input Ownership\n    // - The idea is that instead of \"eating\" a given input, we can link to an owner id.\n    // - Ownership is most often claimed as a result of reacting to a press/down event (but occasionally may be claimed ahead).\n    // - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID.\n    // - Legacy input queries (without specifying an owner or _Any or _None) are equivalent to using ImGuiKeyOwner_Any (== 0).\n    // - Input ownership is automatically released on the frame after a key is released. Therefore:\n    //   - for ownership registration happening as a result of a down/press event, the SetKeyOwner() call may be done once (common case).\n    //   - for ownership registration happening ahead of a down/press event, the SetKeyOwner() call needs to be made every frame (happens if e.g. claiming ownership on hover).\n    // - SetItemKeyOwner() is a shortcut for common simple case. A custom widget will probably want to call SetKeyOwner() multiple times directly based on its interaction state.\n    // - This is marked experimental because not all widgets are fully honoring the Set/Test idioms. We will need to move forward step by step.\n    //   Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved.\n    IMGUI_API ImGuiID           GetKeyOwner(ImGuiKey key);\n    IMGUI_API void              SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);\n    IMGUI_API void              SetKeyOwnersForKeyChord(ImGuiKeyChord key, ImGuiID owner_id, ImGuiInputFlags flags = 0);\n    IMGUI_API void              SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags = 0);           // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'.\n    IMGUI_API bool              TestKeyOwner(ImGuiKey key, ImGuiID owner_id);                       // Test that key is either not owned, either owned by 'owner_id'\n    inline ImGuiKeyOwnerData*   GetKeyOwnerData(ImGuiContext* ctx, ImGuiKey key)                    { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(ctx, key); IM_ASSERT(IsNamedKey(key)); return &ctx->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; }\n\n    // [EXPERIMENTAL] High-Level: Input Access functions w/ support for Key/Input Ownership\n    // - Important: legacy IsKeyPressed(ImGuiKey, bool repeat=true) _DEFAULTS_ to repeat, new IsKeyPressed() requires _EXPLICIT_ ImGuiInputFlags_Repeat flag.\n    // - Expected to be later promoted to public API, the prototypes are designed to replace existing ones (since owner_id can default to Any == 0)\n    // - Specifying a value for 'ImGuiID owner' will test that EITHER the key is NOT owned (UNLESS locked), EITHER the key is owned by 'owner'.\n    //   Legacy functions use ImGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease.\n    // - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API.\n    IMGUI_API bool              IsKeyDown(ImGuiKey key, ImGuiID owner_id);\n    IMGUI_API bool              IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0);    // Important: when transitioning from old to new IsKeyPressed(): old API has \"bool repeat = true\", so would default to repeat. New API requiress explicit ImGuiInputFlags_Repeat.\n    IMGUI_API bool              IsKeyReleased(ImGuiKey key, ImGuiID owner_id);\n    IMGUI_API bool              IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id);\n    IMGUI_API bool              IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0);\n    IMGUI_API bool              IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id);\n    IMGUI_API bool              IsMouseDoubleClicked(ImGuiMouseButton button, ImGuiID owner_id);\n\n    // [EXPERIMENTAL] Shortcut Routing\n    // - ImGuiKeyChord = a ImGuiKey optionally OR-red with ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super.\n    //     ImGuiKey_C                 (accepted by functions taking ImGuiKey or ImGuiKeyChord)\n    //     ImGuiKey_C | ImGuiMod_Ctrl (accepted by functions taking ImGuiKeyChord)\n    //   ONLY ImGuiMod_XXX values are legal to 'OR' with an ImGuiKey. You CANNOT 'OR' two ImGuiKey values.\n    // - When using one of the routing flags (e.g. ImGuiInputFlags_RouteFocused): routes requested ahead of time given a chord (key + modifiers) and a routing policy.\n    // - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame.\n    // - Route is granted to a single owner. When multiple requests are made we have policies to select the winning route.\n    // - Multiple read sites may use the same owner id and will all get the granted route.\n    // - For routing: when owner_id is 0 we use the current Focus Scope ID as a default owner in order to identify our location.\n    // - TL;DR;\n    //   - IsKeyChordPressed() compares mods + call IsKeyPressed() -> function has no side-effect.\n    //   - Shortcut() submits a route then if currently can be routed calls IsKeyChordPressed() -> function has (desirable) side-effects.\n    IMGUI_API bool              IsKeyChordPressed(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags = 0);\n    IMGUI_API bool              Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);\n    IMGUI_API bool              SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0);\n    IMGUI_API bool              TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id);\n    IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord);\n\n    // [EXPERIMENTAL] Focus Scope\n    // This is generally used to identify a unique input location (for e.g. a selection set)\n    // There is one per window (automatically set in Begin), but:\n    // - Selection patterns generally need to react (e.g. clear a selection) when landing on one item of the set.\n    //   So in order to identify a set multiple lists in same window may each need a focus scope.\n    //   If you imagine an hypothetical BeginSelectionGroup()/EndSelectionGroup() api, it would likely call PushFocusScope()/EndFocusScope()\n    // - Shortcut routing also use focus scope as a default location identifier if an owner is not provided.\n    // We don't use the ID Stack for this as it is common to want them separate.\n    IMGUI_API void          PushFocusScope(ImGuiID id);\n    IMGUI_API void          PopFocusScope();\n    inline ImGuiID          GetCurrentFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentFocusScopeId; }   // Focus scope we are outputting into, set by PushFocusScope()\n\n    // Drag and Drop\n    IMGUI_API bool          IsDragDropActive();\n    IMGUI_API bool          BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id);\n    IMGUI_API void          ClearDragDrop();\n    IMGUI_API bool          IsDragDropPayloadBeingAccepted();\n    IMGUI_API void          RenderDragDropTargetRect(const ImRect& bb);\n\n    // Typing-Select API\n    IMGUI_API ImGuiTypingSelectRequest* GetTypingSelectRequest(ImGuiTypingSelectFlags flags = ImGuiTypingSelectFlags_None);\n    IMGUI_API int           TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);\n    IMGUI_API int           TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx);\n    IMGUI_API int           TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data);\n\n    // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API)\n    IMGUI_API void          SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect);\n    IMGUI_API void          BeginColumns(const char* str_id, int count, ImGuiOldColumnFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns().\n    IMGUI_API void          EndColumns();                                                               // close columns\n    IMGUI_API void          PushColumnClipRect(int column_index);\n    IMGUI_API void          PushColumnsBackground();\n    IMGUI_API void          PopColumnsBackground();\n    IMGUI_API ImGuiID       GetColumnsID(const char* str_id, int count);\n    IMGUI_API ImGuiOldColumns* FindOrCreateColumns(ImGuiWindow* window, ImGuiID id);\n    IMGUI_API float         GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm);\n    IMGUI_API float         GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset);\n\n    // Tables: Candidates for public API\n    IMGUI_API void          TableOpenContextMenu(int column_n = -1);\n    IMGUI_API void          TableSetColumnWidth(int column_n, float width);\n    IMGUI_API void          TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs);\n    IMGUI_API int           TableGetHoveredColumn();    // May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.\n    IMGUI_API int           TableGetHoveredRow();       // Retrieve *PREVIOUS FRAME* hovered row. This difference with TableGetHoveredColumn() is the reason why this is not public yet.\n    IMGUI_API float         TableGetHeaderRowHeight();\n    IMGUI_API float         TableGetHeaderAngledMaxLabelWidth();\n    IMGUI_API void          TablePushBackgroundChannel();\n    IMGUI_API void          TablePopBackgroundChannel();\n    IMGUI_API void          TableAngledHeadersRowEx(float angle, float label_width = 0.0f);\n\n    // Tables: Internals\n    inline    ImGuiTable*   GetCurrentTable() { ImGuiContext& g = *GImGui; return g.CurrentTable; }\n    IMGUI_API ImGuiTable*   TableFindByID(ImGuiID id);\n    IMGUI_API bool          BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags = 0, const ImVec2& outer_size = ImVec2(0, 0), float inner_width = 0.0f);\n    IMGUI_API void          TableBeginInitMemory(ImGuiTable* table, int columns_count);\n    IMGUI_API void          TableBeginApplyRequests(ImGuiTable* table);\n    IMGUI_API void          TableSetupDrawChannels(ImGuiTable* table);\n    IMGUI_API void          TableUpdateLayout(ImGuiTable* table);\n    IMGUI_API void          TableUpdateBorders(ImGuiTable* table);\n    IMGUI_API void          TableUpdateColumnsWeightFromWidth(ImGuiTable* table);\n    IMGUI_API void          TableDrawBorders(ImGuiTable* table);\n    IMGUI_API void          TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display);\n    IMGUI_API bool          TableBeginContextMenuPopup(ImGuiTable* table);\n    IMGUI_API void          TableMergeDrawChannels(ImGuiTable* table);\n    inline ImGuiTableInstanceData*  TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; }\n    inline ImGuiID                  TableGetInstanceID(ImGuiTable* table, int instance_no)   { return TableGetInstanceData(table, instance_no)->TableInstanceID; }\n    IMGUI_API void          TableSortSpecsSanitize(ImGuiTable* table);\n    IMGUI_API void          TableSortSpecsBuild(ImGuiTable* table);\n    IMGUI_API ImGuiSortDirection TableGetColumnNextSortDirection(ImGuiTableColumn* column);\n    IMGUI_API void          TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column);\n    IMGUI_API float         TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column);\n    IMGUI_API void          TableBeginRow(ImGuiTable* table);\n    IMGUI_API void          TableEndRow(ImGuiTable* table);\n    IMGUI_API void          TableBeginCell(ImGuiTable* table, int column_n);\n    IMGUI_API void          TableEndCell(ImGuiTable* table);\n    IMGUI_API ImRect        TableGetCellBgRect(const ImGuiTable* table, int column_n);\n    IMGUI_API const char*   TableGetColumnName(const ImGuiTable* table, int column_n);\n    IMGUI_API ImGuiID       TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no = 0);\n    IMGUI_API float         TableGetMaxColumnWidth(const ImGuiTable* table, int column_n);\n    IMGUI_API void          TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n);\n    IMGUI_API void          TableSetColumnWidthAutoAll(ImGuiTable* table);\n    IMGUI_API void          TableRemove(ImGuiTable* table);\n    IMGUI_API void          TableGcCompactTransientBuffers(ImGuiTable* table);\n    IMGUI_API void          TableGcCompactTransientBuffers(ImGuiTableTempData* table);\n    IMGUI_API void          TableGcCompactSettings();\n\n    // Tables: Settings\n    IMGUI_API void                  TableLoadSettings(ImGuiTable* table);\n    IMGUI_API void                  TableSaveSettings(ImGuiTable* table);\n    IMGUI_API void                  TableResetSettings(ImGuiTable* table);\n    IMGUI_API ImGuiTableSettings*   TableGetBoundSettings(ImGuiTable* table);\n    IMGUI_API void                  TableSettingsAddSettingsHandler();\n    IMGUI_API ImGuiTableSettings*   TableSettingsCreate(ImGuiID id, int columns_count);\n    IMGUI_API ImGuiTableSettings*   TableSettingsFindByID(ImGuiID id);\n\n    // Tab Bars\n    inline    ImGuiTabBar*  GetCurrentTabBar() { ImGuiContext& g = *GImGui; return g.CurrentTabBar; }\n    IMGUI_API bool          BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags);\n    IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id);\n    IMGUI_API ImGuiTabItem* TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order);\n    IMGUI_API ImGuiTabItem* TabBarGetCurrentTab(ImGuiTabBar* tab_bar);\n    inline int              TabBarGetTabOrder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { return tab_bar->Tabs.index_from_ptr(tab); }\n    IMGUI_API const char*   TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);\n    IMGUI_API void          TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id);\n    IMGUI_API void          TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);\n    IMGUI_API void          TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab);\n    IMGUI_API void          TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset);\n    IMGUI_API void          TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImVec2 mouse_pos);\n    IMGUI_API bool          TabBarProcessReorder(ImGuiTabBar* tab_bar);\n    IMGUI_API bool          TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window);\n    IMGUI_API ImVec2        TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker);\n    IMGUI_API ImVec2        TabItemCalcSize(ImGuiWindow* window);\n    IMGUI_API void          TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col);\n    IMGUI_API void          TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped);\n\n    // Render helpers\n    // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT.\n    // NB: All position are in absolute pixels coordinates (we are never using window coordinates internally)\n    IMGUI_API void          RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true);\n    IMGUI_API void          RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);\n    IMGUI_API void          RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);\n    IMGUI_API void          RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);\n    IMGUI_API void          RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);\n    IMGUI_API void          RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f);\n    IMGUI_API void          RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);\n    IMGUI_API void          RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);\n    IMGUI_API void          RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_TypeDefault); // Navigation highlight\n    IMGUI_API const char*   FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text.\n    IMGUI_API void          RenderMouseCursor(ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow);\n\n    // Render helpers (those functions don't access any ImGui state!)\n    IMGUI_API void          RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f);\n    IMGUI_API void          RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col);\n    IMGUI_API void          RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz);\n    IMGUI_API void          RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col);\n    IMGUI_API void          RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding);\n    IMGUI_API void          RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding);\n\n    // Widgets\n    IMGUI_API void          TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0);\n    IMGUI_API bool          ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);\n    IMGUI_API bool          ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0);\n    IMGUI_API bool          ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags = 0);\n    IMGUI_API void          SeparatorEx(ImGuiSeparatorFlags flags, float thickness = 1.0f);\n    IMGUI_API void          SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_width);\n    IMGUI_API bool          CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value);\n    IMGUI_API bool          CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value);\n\n    // Widgets: Window Decorations\n    IMGUI_API bool          CloseButton(ImGuiID id, const ImVec2& pos);\n    IMGUI_API bool          CollapseButton(ImGuiID id, const ImVec2& pos);\n    IMGUI_API void          Scrollbar(ImGuiAxis axis);\n    IMGUI_API bool          ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags);\n    IMGUI_API ImRect        GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis);\n    IMGUI_API ImGuiID       GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis);\n    IMGUI_API ImGuiID       GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners\n    IMGUI_API ImGuiID       GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir);\n\n    // Widgets low-level behaviors\n    IMGUI_API bool          ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0);\n    IMGUI_API bool          DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags);\n    IMGUI_API bool          SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);\n    IMGUI_API bool          SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0);\n    IMGUI_API bool          TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL);\n    IMGUI_API void          TreePushOverrideID(ImGuiID id);\n    IMGUI_API void          TreeNodeSetOpen(ImGuiID id, bool open);\n    IMGUI_API bool          TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags);   // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging.\n    IMGUI_API void          SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data);\n\n    // Template functions are instantiated in imgui_widgets.cpp for a finite number of types.\n    // To use them externally (for custom widget) you may need an \"extern template\" statement in your code in order to link to existing instances and silence Clang warnings (see #2036).\n    // e.g. \" extern template IMGUI_API float RoundScalarWithFormatT<float, float>(const char* format, ImGuiDataType data_type, float v); \"\n    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API float ScaleRatioFromValueT(ImGuiDataType data_type, T v, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size);\n    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API T     ScaleValueFromRatioT(ImGuiDataType data_type, float t, T v_min, T v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_size);\n    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API bool  DragBehaviorT(ImGuiDataType data_type, T* v, float v_speed, T v_min, T v_max, const char* format, ImGuiSliderFlags flags);\n    template<typename T, typename SIGNED_T, typename FLOAT_T>   IMGUI_API bool  SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, T* v, T v_min, T v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb);\n    template<typename T>                                        IMGUI_API T     RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, T v);\n    template<typename T>                                        IMGUI_API bool  CheckboxFlagsT(const char* label, T* flags, T flags_value);\n\n    // Data type helpers\n    IMGUI_API const ImGuiDataTypeInfo*  DataTypeGetInfo(ImGuiDataType data_type);\n    IMGUI_API int           DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format);\n    IMGUI_API void          DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg_1, const void* arg_2);\n    IMGUI_API bool          DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format);\n    IMGUI_API int           DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2);\n    IMGUI_API bool          DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max);\n\n    // InputText\n    IMGUI_API bool          InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);\n    IMGUI_API void          InputTextDeactivateHook(ImGuiID id);\n    IMGUI_API bool          TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags);\n    IMGUI_API bool          TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL);\n    inline bool             TempInputIsActive(ImGuiID id)       { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); }\n    inline ImGuiInputTextState* GetInputTextState(ImGuiID id)   { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active\n\n    // Color\n    IMGUI_API void          ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags);\n    IMGUI_API void          ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags);\n    IMGUI_API void          ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags);\n\n    // Plot\n    IMGUI_API int           PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg);\n\n    // Shade functions (write over already created vertices)\n    IMGUI_API void          ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1);\n    IMGUI_API void          ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp);\n    IMGUI_API void          ShadeVertsTransformPos(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& pivot_in, float cos_a, float sin_a, const ImVec2& pivot_out);\n\n    // Garbage collection\n    IMGUI_API void          GcCompactTransientMiscBuffers();\n    IMGUI_API void          GcCompactTransientWindowBuffers(ImGuiWindow* window);\n    IMGUI_API void          GcAwakeTransientWindowBuffers(ImGuiWindow* window);\n\n    // Debug Log\n    IMGUI_API void          DebugLog(const char* fmt, ...) IM_FMTARGS(1);\n    IMGUI_API void          DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1);\n    IMGUI_API void          DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free\n\n    // Debug Tools\n    IMGUI_API void          ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);\n    IMGUI_API void          ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);\n    IMGUI_API void          ErrorCheckUsingSetCursorPosToExtendParentBoundaries();\n    IMGUI_API void          DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255));\n    IMGUI_API void          DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255));\n    IMGUI_API void          DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255));\n    IMGUI_API void          DebugLocateItem(ImGuiID target_id);                     // Call sparingly: only 1 at the same time!\n    IMGUI_API void          DebugLocateItemOnHover(ImGuiID target_id);              // Only call on reaction to a mouse Hover: because only 1 at the same time!\n    IMGUI_API void          DebugLocateItemResolveWithLastItem();\n    inline void             DebugStartItemPicker()                                  { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; }\n    IMGUI_API void          ShowFontAtlas(ImFontAtlas* atlas);\n    IMGUI_API void          DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end);\n    IMGUI_API void          DebugNodeColumns(ImGuiOldColumns* columns);\n    IMGUI_API void          DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label);\n    IMGUI_API void          DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb);\n    IMGUI_API void          DebugNodeFont(ImFont* font);\n    IMGUI_API void          DebugNodeFontGlyph(ImFont* font, const ImFontGlyph* glyph);\n    IMGUI_API void          DebugNodeStorage(ImGuiStorage* storage, const char* label);\n    IMGUI_API void          DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label);\n    IMGUI_API void          DebugNodeTable(ImGuiTable* table);\n    IMGUI_API void          DebugNodeTableSettings(ImGuiTableSettings* settings);\n    IMGUI_API void          DebugNodeInputTextState(ImGuiInputTextState* state);\n    IMGUI_API void          DebugNodeTypingSelectState(ImGuiTypingSelectState* state);\n    IMGUI_API void          DebugNodeWindow(ImGuiWindow* window, const char* label);\n    IMGUI_API void          DebugNodeWindowSettings(ImGuiWindowSettings* settings);\n    IMGUI_API void          DebugNodeWindowsList(ImVector<ImGuiWindow*>* windows, const char* label);\n    IMGUI_API void          DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack);\n    IMGUI_API void          DebugNodeViewport(ImGuiViewportP* viewport);\n    IMGUI_API void          DebugRenderKeyboardPreview(ImDrawList* draw_list);\n    IMGUI_API void          DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb);\n\n    // Obsolete functions\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    inline void     SetItemUsingMouseWheel()                                            { SetItemKeyOwner(ImGuiKey_MouseWheelY); }      // Changed in 1.89\n    inline bool     TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0)    { return TreeNodeUpdateNextOpen(id, flags); }   // Renamed in 1.89\n\n    // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets that used FocusableItemRegister():\n    //  (Old) IMGUI_VERSION_NUM  < 18209: using 'ItemAdd(....)'                              and 'bool tab_focused = FocusableItemRegister(...)'\n    //  (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)'  and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0'\n    //  (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)'     and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput))' (WIP)\n    // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText()\n    inline bool     FocusableItemRegister(ImGuiWindow* window, ImGuiID id)              { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd()\n    inline void     FocusableItemUnregister(ImGuiWindow* window)                        { IM_ASSERT(0); IM_UNUSED(window); }                              // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem\n#endif\n#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO\n    inline bool     IsKeyPressedMap(ImGuiKey key, bool repeat = true)                   { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity!\n#endif\n\n} // namespace ImGui\n\n\n//-----------------------------------------------------------------------------\n// [SECTION] ImFontAtlas internal API\n//-----------------------------------------------------------------------------\n\n// This structure is likely to evolve as we add support for incremental atlas updates\nstruct ImFontBuilderIO\n{\n    bool    (*FontBuilder_Build)(ImFontAtlas* atlas);\n};\n\n// Helper for font builder\n#ifdef IMGUI_ENABLE_STB_TRUETYPE\nIMGUI_API const ImFontBuilderIO* ImFontAtlasGetBuilderForStbTruetype();\n#endif\nIMGUI_API void      ImFontAtlasUpdateConfigDataPointers(ImFontAtlas* atlas);\nIMGUI_API void      ImFontAtlasBuildInit(ImFontAtlas* atlas);\nIMGUI_API void      ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent);\nIMGUI_API void      ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque);\nIMGUI_API void      ImFontAtlasBuildFinish(ImFontAtlas* atlas);\nIMGUI_API void      ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned char in_marker_pixel_value);\nIMGUI_API void      ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value);\nIMGUI_API void      ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor);\nIMGUI_API void      ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride);\n\n//-----------------------------------------------------------------------------\n// [SECTION] Test Engine specific hooks (imgui_test_engine)\n//-----------------------------------------------------------------------------\n\n#ifdef IMGUI_ENABLE_TEST_ENGINE\nextern void         ImGuiTestEngineHook_ItemAdd(ImGuiContext* ctx, ImGuiID id, const ImRect& bb, const ImGuiLastItemData* item_data);           // item_data may be NULL\nextern void         ImGuiTestEngineHook_ItemInfo(ImGuiContext* ctx, ImGuiID id, const char* label, ImGuiItemStatusFlags flags);\nextern void         ImGuiTestEngineHook_Log(ImGuiContext* ctx, const char* fmt, ...);\nextern const char*  ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiID id);\n\n// In IMGUI_VERSION_NUM >= 18934: changed IMGUI_TEST_ENGINE_ITEM_ADD(bb,id) to IMGUI_TEST_ENGINE_ITEM_ADD(id,bb,item_data);\n#define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA)      if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _ID, _BB, _ITEM_DATA)    // Register item bounding box\n#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS)      if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS)    // Register item label and status flags (optional)\n#define IMGUI_TEST_ENGINE_LOG(_FMT,...)                     if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__)           // Custom log entry from user land into test log\n#else\n#define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID)                 ((void)0)\n#define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS)      ((void)g)\n#endif\n\n//-----------------------------------------------------------------------------\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#elif defined(__GNUC__)\n#pragma GCC diagnostic pop\n#endif\n\n#ifdef _MSC_VER\n#pragma warning (pop)\n#endif\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "third-party/imgui/imgui_tables.cpp",
    "content": "// dear imgui, v1.90.0\n// (tables and columns code)\n\n/*\n\nIndex of this file:\n\n// [SECTION] Commentary\n// [SECTION] Header mess\n// [SECTION] Tables: Main code\n// [SECTION] Tables: Simple accessors\n// [SECTION] Tables: Row changes\n// [SECTION] Tables: Columns changes\n// [SECTION] Tables: Columns width management\n// [SECTION] Tables: Drawing\n// [SECTION] Tables: Sorting\n// [SECTION] Tables: Headers\n// [SECTION] Tables: Context Menu\n// [SECTION] Tables: Settings (.ini data)\n// [SECTION] Tables: Garbage Collection\n// [SECTION] Tables: Debugging\n// [SECTION] Columns, BeginColumns, EndColumns, etc.\n\n*/\n\n// Navigating this file:\n// - In Visual Studio IDE: CTRL+comma (\"Edit.GoToAll\") can follow symbols in comments, whereas CTRL+F12 (\"Edit.GoToImplementation\") cannot.\n// - With Visual Assist installed: ALT+G (\"VAssistX.GoToImplementation\") can also follow symbols in comments.\n\n//-----------------------------------------------------------------------------\n// [SECTION] Commentary\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// Typical tables call flow: (root level is generally public API):\n//-----------------------------------------------------------------------------\n// - BeginTable()                               user begin into a table\n//    | BeginChild()                            - (if ScrollX/ScrollY is set)\n//    | TableBeginInitMemory()                  - first time table is used\n//    | TableResetSettings()                    - on settings reset\n//    | TableLoadSettings()                     - on settings load\n//    | TableBeginApplyRequests()               - apply queued resizing/reordering/hiding requests\n//    | - TableSetColumnWidth()                 - apply resizing width (for mouse resize, often requested by previous frame)\n//    |    - TableUpdateColumnsWeightFromWidth()- recompute columns weights (of stretch columns) from their respective width\n// - TableSetupColumn()                         user submit columns details (optional)\n// - TableSetupScrollFreeze()                   user submit scroll freeze information (optional)\n//-----------------------------------------------------------------------------\n// - TableUpdateLayout() [Internal]             followup to BeginTable(): setup everything: widths, columns positions, clipping rectangles. Automatically called by the FIRST call to TableNextRow() or TableHeadersRow().\n//    | TableSetupDrawChannels()                - setup ImDrawList channels\n//    | TableUpdateBorders()                    - detect hovering columns for resize, ahead of contents submission\n//    | TableBeginContextMenuPopup()\n//    | - TableDrawDefaultContextMenu()         - draw right-click context menu contents\n//-----------------------------------------------------------------------------\n// - TableHeadersRow() or TableHeader()         user submit a headers row (optional)\n//    | TableSortSpecsClickColumn()             - when left-clicked: alter sort order and sort direction\n//    | TableOpenContextMenu()                  - when right-clicked: trigger opening of the default context menu\n// - TableGetSortSpecs()                        user queries updated sort specs (optional, generally after submitting headers)\n// - TableNextRow()                             user begin into a new row (also automatically called by TableHeadersRow())\n//    | TableEndRow()                           - finish existing row\n//    | TableBeginRow()                         - add a new row\n// - TableSetColumnIndex() / TableNextColumn()  user begin into a cell\n//    | TableEndCell()                          - close existing column/cell\n//    | TableBeginCell()                        - enter into current column/cell\n// - [...]                                      user emit contents\n//-----------------------------------------------------------------------------\n// - EndTable()                                 user ends the table\n//    | TableDrawBorders()                      - draw outer borders, inner vertical borders\n//    | TableMergeDrawChannels()                - merge draw channels if clipping isn't required\n//    | EndChild()                              - (if ScrollX/ScrollY is set)\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// TABLE SIZING\n//-----------------------------------------------------------------------------\n// (Read carefully because this is subtle but it does make sense!)\n//-----------------------------------------------------------------------------\n// About 'outer_size':\n// Its meaning needs to differ slightly depending on if we are using ScrollX/ScrollY flags.\n// Default value is ImVec2(0.0f, 0.0f).\n//   X\n//   - outer_size.x <= 0.0f  ->  Right-align from window/work-rect right-most edge. With -FLT_MIN or 0.0f will align exactly on right-most edge.\n//   - outer_size.x  > 0.0f  ->  Set Fixed width.\n//   Y with ScrollX/ScrollY disabled: we output table directly in current window\n//   - outer_size.y  < 0.0f  ->  Bottom-align (but will auto extend, unless _NoHostExtendY is set). Not meaningful if parent window can vertically scroll.\n//   - outer_size.y  = 0.0f  ->  No minimum height (but will auto extend, unless _NoHostExtendY is set)\n//   - outer_size.y  > 0.0f  ->  Set Minimum height (but will auto extend, unless _NoHostExtendY is set)\n//   Y with ScrollX/ScrollY enabled: using a child window for scrolling\n//   - outer_size.y  < 0.0f  ->  Bottom-align. Not meaningful if parent window can vertically scroll.\n//   - outer_size.y  = 0.0f  ->  Bottom-align, consistent with BeginChild(). Not recommended unless table is last item in parent window.\n//   - outer_size.y  > 0.0f  ->  Set Exact height. Recommended when using Scrolling on any axis.\n//-----------------------------------------------------------------------------\n// Outer size is also affected by the NoHostExtendX/NoHostExtendY flags.\n// Important to note how the two flags have slightly different behaviors!\n//   - ImGuiTableFlags_NoHostExtendX -> Make outer width auto-fit to columns (overriding outer_size.x value). Only available when ScrollX/ScrollY are disabled and Stretch columns are not used.\n//   - ImGuiTableFlags_NoHostExtendY -> Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit). Only available when ScrollX/ScrollY is disabled. Data below the limit will be clipped and not visible.\n// In theory ImGuiTableFlags_NoHostExtendY could be the default and any non-scrolling tables with outer_size.y != 0.0f would use exact height.\n// This would be consistent but perhaps less useful and more confusing (as vertically clipped items are not useful and not easily noticeable).\n//-----------------------------------------------------------------------------\n// About 'inner_width':\n//   With ScrollX disabled:\n//   - inner_width          ->  *ignored*\n//   With ScrollX enabled:\n//   - inner_width  < 0.0f  ->  *illegal* fit in known width (right align from outer_size.x) <-- weird\n//   - inner_width  = 0.0f  ->  fit in outer_width: Fixed size columns will take space they need (if avail, otherwise shrink down), Stretch columns becomes Fixed columns.\n//   - inner_width  > 0.0f  ->  override scrolling width, generally to be larger than outer_size.x. Fixed column take space they need (if avail, otherwise shrink down), Stretch columns share remaining space!\n//-----------------------------------------------------------------------------\n// Details:\n// - If you want to use Stretch columns with ScrollX, you generally need to specify 'inner_width' otherwise the concept\n//   of \"available space\" doesn't make sense.\n// - Even if not really useful, we allow 'inner_width < outer_size.x' for consistency and to facilitate understanding\n//   of what the value does.\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// COLUMNS SIZING POLICIES\n// (Reference: ImGuiTableFlags_SizingXXX flags and ImGuiTableColumnFlags_WidthXXX flags)\n//-----------------------------------------------------------------------------\n// About overriding column sizing policy and width/weight with TableSetupColumn():\n// We use a default parameter of -1 for 'init_width'/'init_weight'.\n//   - with ImGuiTableColumnFlags_WidthFixed,    init_width  <= 0 (default)  --> width is automatic\n//   - with ImGuiTableColumnFlags_WidthFixed,    init_width  >  0 (explicit) --> width is custom\n//   - with ImGuiTableColumnFlags_WidthStretch,  init_weight <= 0 (default)  --> weight is 1.0f\n//   - with ImGuiTableColumnFlags_WidthStretch,  init_weight >  0 (explicit) --> weight is custom\n// Widths are specified _without_ CellPadding. If you specify a width of 100.0f, the column will be cover (100.0f + Padding * 2.0f)\n// and you can fit a 100.0f wide item in it without clipping and with padding honored.\n//-----------------------------------------------------------------------------\n// About default sizing policy (if you don't specify a ImGuiTableColumnFlags_WidthXXXX flag)\n//   - with Table policy ImGuiTableFlags_SizingFixedFit      --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is equal to contents width\n//   - with Table policy ImGuiTableFlags_SizingFixedSame     --> default Column policy is ImGuiTableColumnFlags_WidthFixed, default Width is max of all contents width\n//   - with Table policy ImGuiTableFlags_SizingStretchSame   --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is 1.0f\n//   - with Table policy ImGuiTableFlags_SizingStretchWeight --> default Column policy is ImGuiTableColumnFlags_WidthStretch, default Weight is proportional to contents\n// Default Width and default Weight can be overridden when calling TableSetupColumn().\n//-----------------------------------------------------------------------------\n// About mixing Fixed/Auto and Stretch columns together:\n//   - the typical use of mixing sizing policies is: any number of LEADING Fixed columns, followed by one or two TRAILING Stretch columns.\n//   - using mixed policies with ScrollX does not make much sense, as using Stretch columns with ScrollX does not make much sense in the first place!\n//     that is, unless 'inner_width' is passed to BeginTable() to explicitly provide a total width to layout columns in.\n//   - when using ImGuiTableFlags_SizingFixedSame with mixed columns, only the Fixed/Auto columns will match their widths to the width of the maximum contents.\n//   - when using ImGuiTableFlags_SizingStretchSame with mixed columns, only the Stretch columns will match their weights/widths.\n//-----------------------------------------------------------------------------\n// About using column width:\n// If a column is manually resizable or has a width specified with TableSetupColumn():\n//   - you may use GetContentRegionAvail().x to query the width available in a given column.\n//   - right-side alignment features such as SetNextItemWidth(-x) or PushItemWidth(-x) will rely on this width.\n// If the column is not resizable and has no width specified with TableSetupColumn():\n//   - its width will be automatic and be set to the max of items submitted.\n//   - therefore you generally cannot have ALL items of the columns use e.g. SetNextItemWidth(-FLT_MIN).\n//   - but if the column has one or more items of known/fixed size, this will become the reference width used by SetNextItemWidth(-FLT_MIN).\n//-----------------------------------------------------------------------------\n\n\n//-----------------------------------------------------------------------------\n// TABLES CLIPPING/CULLING\n//-----------------------------------------------------------------------------\n// About clipping/culling of Rows in Tables:\n// - For large numbers of rows, it is recommended you use ImGuiListClipper to submit only visible rows.\n//   ImGuiListClipper is reliant on the fact that rows are of equal height.\n//   See 'Demo->Tables->Vertical Scrolling' or 'Demo->Tables->Advanced' for a demo of using the clipper.\n// - Note that auto-resizing columns don't play well with using the clipper.\n//   By default a table with _ScrollX but without _Resizable will have column auto-resize.\n//   So, if you want to use the clipper, make sure to either enable _Resizable, either setup columns width explicitly with _WidthFixed.\n//-----------------------------------------------------------------------------\n// About clipping/culling of Columns in Tables:\n// - Both TableSetColumnIndex() and TableNextColumn() return true when the column is visible or performing\n//   width measurements. Otherwise, you may skip submitting the contents of a cell/column, BUT ONLY if you know\n//   it is not going to contribute to row height.\n//   In many situations, you may skip submitting contents for every column but one (e.g. the first one).\n// - Case A: column is not hidden by user, and at least partially in sight (most common case).\n// - Case B: column is clipped / out of sight (because of scrolling or parent ClipRect): TableNextColumn() return false as a hint but we still allow layout output.\n// - Case C: column is hidden explicitly by the user (e.g. via the context menu, or _DefaultHide column flag, etc.).\n//\n//                        [A]         [B]          [C]\n//  TableNextColumn():    true        false        false       -> [userland] when TableNextColumn() / TableSetColumnIndex() returns false, user can skip submitting items but only if the column doesn't contribute to row height.\n//          SkipItems:    false       false        true        -> [internal] when SkipItems is true, most widgets will early out if submitted, resulting is no layout output.\n//           ClipRect:    normal      zero-width   zero-width  -> [internal] when ClipRect is zero, ItemAdd() will return false and most widgets will early out mid-way.\n//  ImDrawList output:    normal      dummy        dummy       -> [internal] when using the dummy channel, ImDrawList submissions (if any) will be wasted (because cliprect is zero-width anyway).\n//\n// - We need to distinguish those cases because non-hidden columns that are clipped outside of scrolling bounds should still contribute their height to the row.\n//   However, in the majority of cases, the contribution to row height is the same for all columns, or the tallest cells are known by the programmer.\n//-----------------------------------------------------------------------------\n// About clipping/culling of whole Tables:\n// - Scrolling tables with a known outer size can be clipped earlier as BeginTable() will return false.\n//-----------------------------------------------------------------------------\n\n//-----------------------------------------------------------------------------\n// [SECTION] Header mess\n//-----------------------------------------------------------------------------\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#ifndef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS\n#endif\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_internal.h\"\n\n// System includes\n#include <stdint.h>     // intptr_t\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4127)     // condition expression is constant\n#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later\n#pragma warning (disable: 5054)     // operator '|': deprecated between enumerations of different types\n#endif\n#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).\n#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast                            // yes, they are more terse.\n#pragma clang diagnostic ignored \"-Wfloat-equal\"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.\n#pragma clang diagnostic ignored \"-Wformat-nonliteral\"              // warning: format string is not a string literal            // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.\n#pragma clang diagnostic ignored \"-Wsign-conversion\"                // warning: implicit conversion changes signedness\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.\n#pragma clang diagnostic ignored \"-Wenum-enum-conversion\"           // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')\n#pragma clang diagnostic ignored \"-Wdeprecated-enum-enum-conversion\"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#elif defined(__GNUC__)\n#pragma GCC diagnostic ignored \"-Wpragmas\"                          // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wformat-nonliteral\"                // warning: format not a string literal, format string not checked\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#endif\n\n//-----------------------------------------------------------------------------\n// [SECTION] Tables: Main code\n//-----------------------------------------------------------------------------\n// - TableFixFlags() [Internal]\n// - TableFindByID() [Internal]\n// - BeginTable()\n// - BeginTableEx() [Internal]\n// - TableBeginInitMemory() [Internal]\n// - TableBeginApplyRequests() [Internal]\n// - TableSetupColumnFlags() [Internal]\n// - TableUpdateLayout() [Internal]\n// - TableUpdateBorders() [Internal]\n// - EndTable()\n// - TableSetupColumn()\n// - TableSetupScrollFreeze()\n//-----------------------------------------------------------------------------\n\n// Configuration\nstatic const int TABLE_DRAW_CHANNEL_BG0 = 0;\nstatic const int TABLE_DRAW_CHANNEL_BG2_FROZEN = 1;\nstatic const int TABLE_DRAW_CHANNEL_NOCLIP = 2;                     // When using ImGuiTableFlags_NoClip (this becomes the last visible channel)\nstatic const float TABLE_BORDER_SIZE                     = 1.0f;    // FIXME-TABLE: Currently hard-coded because of clipping assumptions with outer borders rendering.\nstatic const float TABLE_RESIZE_SEPARATOR_HALF_THICKNESS = 4.0f;    // Extend outside inner borders.\nstatic const float TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER = 0.06f;   // Delay/timer before making the hover feedback (color+cursor) visible because tables/columns tends to be more cramped.\n\n// Helper\ninline ImGuiTableFlags TableFixFlags(ImGuiTableFlags flags, ImGuiWindow* outer_window)\n{\n    // Adjust flags: set default sizing policy\n    if ((flags & ImGuiTableFlags_SizingMask_) == 0)\n        flags |= ((flags & ImGuiTableFlags_ScrollX) || (outer_window->Flags & ImGuiWindowFlags_AlwaysAutoResize)) ? ImGuiTableFlags_SizingFixedFit : ImGuiTableFlags_SizingStretchSame;\n\n    // Adjust flags: enable NoKeepColumnsVisible when using ImGuiTableFlags_SizingFixedSame\n    if ((flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)\n        flags |= ImGuiTableFlags_NoKeepColumnsVisible;\n\n    // Adjust flags: enforce borders when resizable\n    if (flags & ImGuiTableFlags_Resizable)\n        flags |= ImGuiTableFlags_BordersInnerV;\n\n    // Adjust flags: disable NoHostExtendX/NoHostExtendY if we have any scrolling going on\n    if (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY))\n        flags &= ~(ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_NoHostExtendY);\n\n    // Adjust flags: NoBordersInBodyUntilResize takes priority over NoBordersInBody\n    if (flags & ImGuiTableFlags_NoBordersInBodyUntilResize)\n        flags &= ~ImGuiTableFlags_NoBordersInBody;\n\n    // Adjust flags: disable saved settings if there's nothing to save\n    if ((flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Sortable)) == 0)\n        flags |= ImGuiTableFlags_NoSavedSettings;\n\n    // Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set)\n    if (outer_window->RootWindow->Flags & ImGuiWindowFlags_NoSavedSettings)\n        flags |= ImGuiTableFlags_NoSavedSettings;\n\n    return flags;\n}\n\nImGuiTable* ImGui::TableFindByID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    return g.Tables.GetByKey(id);\n}\n\n// Read about \"TABLE SIZING\" at the top of this file.\nbool    ImGui::BeginTable(const char* str_id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width)\n{\n    ImGuiID id = GetID(str_id);\n    return BeginTableEx(str_id, id, columns_count, flags, outer_size, inner_width);\n}\n\nbool    ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImGuiTableFlags flags, const ImVec2& outer_size, float inner_width)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* outer_window = GetCurrentWindow();\n    if (outer_window->SkipItems) // Consistent with other tables + beneficial side effect that assert on miscalling EndTable() will be more visible.\n        return false;\n\n    // Sanity checks\n    IM_ASSERT(columns_count > 0 && columns_count < IMGUI_TABLE_MAX_COLUMNS);\n    if (flags & ImGuiTableFlags_ScrollX)\n        IM_ASSERT(inner_width >= 0.0f);\n\n    // If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve.\n    const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;\n    const ImVec2 avail_size = GetContentRegionAvail();\n    const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);\n    const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);\n    const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows!\n    if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)\n    {\n        ItemSize(outer_rect);\n        return false;\n    }\n\n    // Acquire storage for the table\n    ImGuiTable* table = g.Tables.GetOrAddByKey(id);\n    const ImGuiTableFlags table_last_flags = table->Flags;\n\n    // Acquire temporary buffers\n    const int table_idx = g.Tables.GetIndex(table);\n    if (++g.TablesTempDataStacked > g.TablesTempData.Size)\n        g.TablesTempData.resize(g.TablesTempDataStacked, ImGuiTableTempData());\n    ImGuiTableTempData* temp_data = table->TempData = &g.TablesTempData[g.TablesTempDataStacked - 1];\n    temp_data->TableIndex = table_idx;\n    table->DrawSplitter = &table->TempData->DrawSplitter;\n    table->DrawSplitter->Clear();\n\n    // Fix flags\n    table->IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0;\n    flags = TableFixFlags(flags, outer_window);\n\n    // Initialize\n    const int previous_frame_active = table->LastFrameActive;\n    const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1;\n    table->ID = id;\n    table->Flags = flags;\n    table->LastFrameActive = g.FrameCount;\n    table->OuterWindow = table->InnerWindow = outer_window;\n    table->ColumnsCount = columns_count;\n    table->IsLayoutLocked = false;\n    table->InnerWidth = inner_width;\n    temp_data->UserOuterSize = outer_size;\n\n    // Instance data (for instance 0, TableID == TableInstanceID)\n    ImGuiID instance_id;\n    table->InstanceCurrent = (ImS16)instance_no;\n    if (instance_no > 0)\n    {\n        IM_ASSERT(table->ColumnsCount == columns_count && \"BeginTable(): Cannot change columns count mid-frame while preserving same ID\");\n        if (table->InstanceDataExtra.Size < instance_no)\n            table->InstanceDataExtra.push_back(ImGuiTableInstanceData());\n        instance_id = GetIDWithSeed(instance_no, GetIDWithSeed(\"##Instances\", NULL, id)); // Push \"##Instances\" followed by (int)instance_no in ID stack.\n    }\n    else\n    {\n        instance_id = id;\n    }\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    table_instance->TableInstanceID = instance_id;\n\n    // When not using a child window, WorkRect.Max will grow as we append contents.\n    if (use_child_window)\n    {\n        // Ensure no vertical scrollbar appears if we only want horizontal one, to make flag consistent\n        // (we have no other way to disable vertical scrollbar of a window while keeping the horizontal one showing)\n        ImVec2 override_content_size(FLT_MAX, FLT_MAX);\n        if ((flags & ImGuiTableFlags_ScrollX) && !(flags & ImGuiTableFlags_ScrollY))\n            override_content_size.y = FLT_MIN;\n\n        // Ensure specified width (when not specified, Stretched columns will act as if the width == OuterWidth and\n        // never lead to any scrolling). We don't handle inner_width < 0.0f, we could potentially use it to right-align\n        // based on the right side of the child window work rect, which would require knowing ahead if we are going to\n        // have decoration taking horizontal spaces (typically a vertical scrollbar).\n        if ((flags & ImGuiTableFlags_ScrollX) && inner_width > 0.0f)\n            override_content_size.x = inner_width;\n\n        if (override_content_size.x != FLT_MAX || override_content_size.y != FLT_MAX)\n            SetNextWindowContentSize(ImVec2(override_content_size.x != FLT_MAX ? override_content_size.x : 0.0f, override_content_size.y != FLT_MAX ? override_content_size.y : 0.0f));\n\n        // Reset scroll if we are reactivating it\n        if ((table_last_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)\n            SetNextWindowScroll(ImVec2(0.0f, 0.0f));\n\n        // Create scrolling region (without border and zero window padding)\n        ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;\n        BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags);\n        table->InnerWindow = g.CurrentWindow;\n        table->WorkRect = table->InnerWindow->WorkRect;\n        table->OuterRect = table->InnerWindow->Rect();\n        table->InnerRect = table->InnerWindow->InnerRect;\n        IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f);\n\n        // Allow submitting when host is measuring\n        if (table->InnerWindow->SkipItems && outer_window_is_measuring_size)\n            table->InnerWindow->SkipItems = false;\n\n        // When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned)\n        if (instance_no == 0)\n        {\n            table->HasScrollbarYPrev = table->HasScrollbarYCurr;\n            table->HasScrollbarYCurr = false;\n        }\n        table->HasScrollbarYCurr |= table->InnerWindow->ScrollbarY;\n    }\n    else\n    {\n        // For non-scrolling tables, WorkRect == OuterRect == InnerRect.\n        // But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().\n        table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;\n    }\n\n    // Push a standardized ID for both child-using and not-child-using tables\n    PushOverrideID(id);\n    if (instance_no > 0)\n        PushOverrideID(instance_id); // FIXME: Somehow this is not resolved by stack-tool, even tho GetIDWithSeed() submitted the symbol.\n\n    // Backup a copy of host window members we will modify\n    ImGuiWindow* inner_window = table->InnerWindow;\n    table->HostIndentX = inner_window->DC.Indent.x;\n    table->HostClipRect = inner_window->ClipRect;\n    table->HostSkipItems = inner_window->SkipItems;\n    temp_data->HostBackupWorkRect = inner_window->WorkRect;\n    temp_data->HostBackupParentWorkRect = inner_window->ParentWorkRect;\n    temp_data->HostBackupColumnsOffset = outer_window->DC.ColumnsOffset;\n    temp_data->HostBackupPrevLineSize = inner_window->DC.PrevLineSize;\n    temp_data->HostBackupCurrLineSize = inner_window->DC.CurrLineSize;\n    temp_data->HostBackupCursorMaxPos = inner_window->DC.CursorMaxPos;\n    temp_data->HostBackupItemWidth = outer_window->DC.ItemWidth;\n    temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;\n    inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);\n\n    // Make left and top borders not overlap our contents by offsetting HostClipRect (#6765)\n    // (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the\n    // limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap\n    // problem only affect scrolling tables in this case we can get away with doing it without extra cost).\n    if (inner_window != outer_window)\n    {\n        if (flags & ImGuiTableFlags_BordersOuterV)\n            table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x);\n        if (flags & ImGuiTableFlags_BordersOuterH)\n            table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y);\n    }\n\n    // Padding and Spacing\n    // - None               ........Content..... Pad .....Content........\n    // - PadOuter           | Pad ..Content..... Pad .....Content.. Pad |\n    // - PadInner           ........Content.. Pad | Pad ..Content........\n    // - PadOuter+PadInner  | Pad ..Content.. Pad | Pad ..Content.. Pad |\n    const bool pad_outer_x = (flags & ImGuiTableFlags_NoPadOuterX) ? false : (flags & ImGuiTableFlags_PadOuterX) ? true : (flags & ImGuiTableFlags_BordersOuterV) != 0;\n    const bool pad_inner_x = (flags & ImGuiTableFlags_NoPadInnerX) ? false : true;\n    const float inner_spacing_for_border = (flags & ImGuiTableFlags_BordersInnerV) ? TABLE_BORDER_SIZE : 0.0f;\n    const float inner_spacing_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) == 0) ? g.Style.CellPadding.x : 0.0f;\n    const float inner_padding_explicit = (pad_inner_x && (flags & ImGuiTableFlags_BordersInnerV) != 0) ? g.Style.CellPadding.x : 0.0f;\n    table->CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border;\n    table->CellSpacingX2 = inner_spacing_explicit;\n    table->CellPaddingX = inner_padding_explicit;\n\n    const float outer_padding_for_border = (flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;\n    const float outer_padding_explicit = pad_outer_x ? g.Style.CellPadding.x : 0.0f;\n    table->OuterPaddingX = (outer_padding_for_border + outer_padding_explicit) - table->CellPaddingX;\n\n    table->CurrentColumn = -1;\n    table->CurrentRow = -1;\n    table->RowBgColorCounter = 0;\n    table->LastRowFlags = ImGuiTableRowFlags_None;\n    table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect;\n    table->InnerClipRect.ClipWith(table->WorkRect);     // We need this to honor inner_width\n    table->InnerClipRect.ClipWithFull(table->HostClipRect);\n    table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y;\n\n    table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow\n    table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow()\n    table->RowCellPaddingY = 0.0f;\n    table->FreezeRowsRequest = table->FreezeRowsCount = 0; // This will be setup by TableSetupScrollFreeze(), if any\n    table->FreezeColumnsRequest = table->FreezeColumnsCount = 0;\n    table->IsUnfrozenRows = true;\n    table->DeclColumnsCount = table->AngledHeadersCount = 0;\n    if (previous_frame_active + 1 < g.FrameCount)\n        table->IsActiveIdInTable = false;\n    temp_data->AngledheadersExtraWidth = 0.0f;\n\n    // Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders()\n    table->BorderColorStrong = GetColorU32(ImGuiCol_TableBorderStrong);\n    table->BorderColorLight = GetColorU32(ImGuiCol_TableBorderLight);\n\n    // Make table current\n    g.CurrentTable = table;\n    outer_window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();\n    outer_window->DC.CurrentTableIdx = table_idx;\n    if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly.\n        inner_window->DC.CurrentTableIdx = table_idx;\n\n    if ((table_last_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0)\n        table->IsResetDisplayOrderRequest = true;\n\n    // Mark as used to avoid GC\n    if (table_idx >= g.TablesLastTimeActive.Size)\n        g.TablesLastTimeActive.resize(table_idx + 1, -1.0f);\n    g.TablesLastTimeActive[table_idx] = (float)g.Time;\n    temp_data->LastTimeActive = (float)g.Time;\n    table->MemoryCompacted = false;\n\n    // Setup memory buffer (clear data if columns count changed)\n    ImGuiTableColumn* old_columns_to_preserve = NULL;\n    void* old_columns_raw_data = NULL;\n    const int old_columns_count = table->Columns.size();\n    if (old_columns_count != 0 && old_columns_count != columns_count)\n    {\n        // Attempt to preserve width on column count change (#4046)\n        old_columns_to_preserve = table->Columns.Data;\n        old_columns_raw_data = table->RawData;\n        table->RawData = NULL;\n    }\n    if (table->RawData == NULL)\n    {\n        TableBeginInitMemory(table, columns_count);\n        table->IsInitializing = table->IsSettingsRequestLoad = true;\n    }\n    if (table->IsResetAllRequest)\n        TableResetSettings(table);\n    if (table->IsInitializing)\n    {\n        // Initialize\n        table->SettingsOffset = -1;\n        table->IsSortSpecsDirty = true;\n        table->InstanceInteracted = -1;\n        table->ContextPopupColumn = -1;\n        table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1;\n        table->AutoFitSingleColumn = -1;\n        table->HoveredColumnBody = table->HoveredColumnBorder = -1;\n        for (int n = 0; n < columns_count; n++)\n        {\n            ImGuiTableColumn* column = &table->Columns[n];\n            if (old_columns_to_preserve && n < old_columns_count)\n            {\n                // FIXME: We don't attempt to preserve column order in this path.\n                *column = old_columns_to_preserve[n];\n            }\n            else\n            {\n                float width_auto = column->WidthAuto;\n                *column = ImGuiTableColumn();\n                column->WidthAuto = width_auto;\n                column->IsPreserveWidthAuto = true; // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker\n                column->IsEnabled = column->IsUserEnabled = column->IsUserEnabledNextFrame = true;\n            }\n            column->DisplayOrder = table->DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)n;\n        }\n    }\n    if (old_columns_raw_data)\n        IM_FREE(old_columns_raw_data);\n\n    // Load settings\n    if (table->IsSettingsRequestLoad)\n        TableLoadSettings(table);\n\n    // Handle DPI/font resize\n    // This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well.\n    // It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition.\n    // FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory.\n    // This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path.\n    const float new_ref_scale_unit = g.FontSize; // g.Font->GetCharAdvance('A') ?\n    if (table->RefScale != 0.0f && table->RefScale != new_ref_scale_unit)\n    {\n        const float scale_factor = new_ref_scale_unit / table->RefScale;\n        //IMGUI_DEBUG_PRINT(\"[table] %08X RefScaleUnit %.3f -> %.3f, scaling width by %.3f\\n\", table->ID, table->RefScaleUnit, new_ref_scale_unit, scale_factor);\n        for (int n = 0; n < columns_count; n++)\n            table->Columns[n].WidthRequest = table->Columns[n].WidthRequest * scale_factor;\n    }\n    table->RefScale = new_ref_scale_unit;\n\n    // Disable output until user calls TableNextRow() or TableNextColumn() leading to the TableUpdateLayout() call..\n    // This is not strictly necessary but will reduce cases were \"out of table\" output will be misleading to the user.\n    // Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option.\n    inner_window->SkipItems = true;\n\n    // Clear names\n    // At this point the ->NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn()\n    if (table->ColumnsNames.Buf.Size > 0)\n        table->ColumnsNames.Buf.resize(0);\n\n    // Apply queued resizing/reordering/hiding requests\n    TableBeginApplyRequests(table);\n\n    return true;\n}\n\n// For reference, the average total _allocation count_ for a table is:\n// + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables[])\n// + 1 (for table->RawData allocated below)\n// + 1 (for table->ColumnsNames, if names are used)\n// Shared allocations for the maximum number of simultaneously nested tables (generally a very small number)\n// + 1 (for table->Splitter._Channels)\n// + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels)\n// Where active_channels_count is variable but often == columns_count or == columns_count + 1, see TableSetupDrawChannels() for details.\n// Unused channels don't perform their +2 allocations.\nvoid ImGui::TableBeginInitMemory(ImGuiTable* table, int columns_count)\n{\n    // Allocate single buffer for our arrays\n    const int columns_bit_array_size = (int)ImBitArrayGetStorageSizeInBytes(columns_count);\n    ImSpanAllocator<6> span_allocator;\n    span_allocator.Reserve(0, columns_count * sizeof(ImGuiTableColumn));\n    span_allocator.Reserve(1, columns_count * sizeof(ImGuiTableColumnIdx));\n    span_allocator.Reserve(2, columns_count * sizeof(ImGuiTableCellData), 4);\n    for (int n = 3; n < 6; n++)\n        span_allocator.Reserve(n, columns_bit_array_size);\n    table->RawData = IM_ALLOC(span_allocator.GetArenaSizeInBytes());\n    memset(table->RawData, 0, span_allocator.GetArenaSizeInBytes());\n    span_allocator.SetArenaBasePtr(table->RawData);\n    span_allocator.GetSpan(0, &table->Columns);\n    span_allocator.GetSpan(1, &table->DisplayOrderToIndex);\n    span_allocator.GetSpan(2, &table->RowCellData);\n    table->EnabledMaskByDisplayOrder = (ImU32*)span_allocator.GetSpanPtrBegin(3);\n    table->EnabledMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(4);\n    table->VisibleMaskByIndex = (ImU32*)span_allocator.GetSpanPtrBegin(5);\n}\n\n// Apply queued resizing/reordering/hiding requests\nvoid ImGui::TableBeginApplyRequests(ImGuiTable* table)\n{\n    // Handle resizing request\n    // (We process this in the TableBegin() of the first instance of each table)\n    // FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling?\n    if (table->InstanceCurrent == 0)\n    {\n        if (table->ResizedColumn != -1 && table->ResizedColumnNextWidth != FLT_MAX)\n            TableSetColumnWidth(table->ResizedColumn, table->ResizedColumnNextWidth);\n        table->LastResizedColumn = table->ResizedColumn;\n        table->ResizedColumnNextWidth = FLT_MAX;\n        table->ResizedColumn = -1;\n\n        // Process auto-fit for single column, which is a special case for stretch columns and fixed columns with FixedSame policy.\n        // FIXME-TABLE: Would be nice to redistribute available stretch space accordingly to other weights, instead of giving it all to siblings.\n        if (table->AutoFitSingleColumn != -1)\n        {\n            TableSetColumnWidth(table->AutoFitSingleColumn, table->Columns[table->AutoFitSingleColumn].WidthAuto);\n            table->AutoFitSingleColumn = -1;\n        }\n    }\n\n    // Handle reordering request\n    // Note: we don't clear ReorderColumn after handling the request.\n    if (table->InstanceCurrent == 0)\n    {\n        if (table->HeldHeaderColumn == -1 && table->ReorderColumn != -1)\n            table->ReorderColumn = -1;\n        table->HeldHeaderColumn = -1;\n        if (table->ReorderColumn != -1 && table->ReorderColumnDir != 0)\n        {\n            // We need to handle reordering across hidden columns.\n            // In the configuration below, moving C to the right of E will lead to:\n            //    ... C [D] E  --->  ... [D] E  C   (Column name/index)\n            //    ... 2  3  4        ...  2  3  4   (Display order)\n            const int reorder_dir = table->ReorderColumnDir;\n            IM_ASSERT(reorder_dir == -1 || reorder_dir == +1);\n            IM_ASSERT(table->Flags & ImGuiTableFlags_Reorderable);\n            ImGuiTableColumn* src_column = &table->Columns[table->ReorderColumn];\n            ImGuiTableColumn* dst_column = &table->Columns[(reorder_dir == -1) ? src_column->PrevEnabledColumn : src_column->NextEnabledColumn];\n            IM_UNUSED(dst_column);\n            const int src_order = src_column->DisplayOrder;\n            const int dst_order = dst_column->DisplayOrder;\n            src_column->DisplayOrder = (ImGuiTableColumnIdx)dst_order;\n            for (int order_n = src_order + reorder_dir; order_n != dst_order + reorder_dir; order_n += reorder_dir)\n                table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder -= (ImGuiTableColumnIdx)reorder_dir;\n            IM_ASSERT(dst_column->DisplayOrder == dst_order - reorder_dir);\n\n            // Display order is stored in both columns->IndexDisplayOrder and table->DisplayOrder[]. Rebuild later from the former.\n            for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n                table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n;\n            table->ReorderColumnDir = 0;\n            table->IsSettingsDirty = true;\n        }\n    }\n\n    // Handle display order reset request\n    if (table->IsResetDisplayOrderRequest)\n    {\n        for (int n = 0; n < table->ColumnsCount; n++)\n            table->DisplayOrderToIndex[n] = table->Columns[n].DisplayOrder = (ImGuiTableColumnIdx)n;\n        table->IsResetDisplayOrderRequest = false;\n        table->IsSettingsDirty = true;\n    }\n}\n\n// Adjust flags: default width mode + stretch columns are not allowed when auto extending\nstatic void TableSetupColumnFlags(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags flags_in)\n{\n    ImGuiTableColumnFlags flags = flags_in;\n\n    // Sizing Policy\n    if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0)\n    {\n        const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_);\n        if (table_sizing_policy == ImGuiTableFlags_SizingFixedFit || table_sizing_policy == ImGuiTableFlags_SizingFixedSame)\n            flags |= ImGuiTableColumnFlags_WidthFixed;\n        else\n            flags |= ImGuiTableColumnFlags_WidthStretch;\n    }\n    else\n    {\n        IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_WidthMask_)); // Check that only 1 of each set is used.\n    }\n\n    // Resize\n    if ((table->Flags & ImGuiTableFlags_Resizable) == 0)\n        flags |= ImGuiTableColumnFlags_NoResize;\n\n    // Sorting\n    if ((flags & ImGuiTableColumnFlags_NoSortAscending) && (flags & ImGuiTableColumnFlags_NoSortDescending))\n        flags |= ImGuiTableColumnFlags_NoSort;\n\n    // Indentation\n    if ((flags & ImGuiTableColumnFlags_IndentMask_) == 0)\n        flags |= (table->Columns.index_from_ptr(column) == 0) ? ImGuiTableColumnFlags_IndentEnable : ImGuiTableColumnFlags_IndentDisable;\n\n    // Alignment\n    //if ((flags & ImGuiTableColumnFlags_AlignMask_) == 0)\n    //    flags |= ImGuiTableColumnFlags_AlignCenter;\n    //IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_AlignMask_)); // Check that only 1 of each set is used.\n\n    // Preserve status flags\n    column->Flags = flags | (column->Flags & ImGuiTableColumnFlags_StatusMask_);\n\n    // Build an ordered list of available sort directions\n    column->SortDirectionsAvailCount = column->SortDirectionsAvailMask = column->SortDirectionsAvailList = 0;\n    if (table->Flags & ImGuiTableFlags_Sortable)\n    {\n        int count = 0, mask = 0, list = 0;\n        if ((flags & ImGuiTableColumnFlags_PreferSortAscending)  != 0 && (flags & ImGuiTableColumnFlags_NoSortAscending)  == 0) { mask |= 1 << ImGuiSortDirection_Ascending;  list |= ImGuiSortDirection_Ascending  << (count << 1); count++; }\n        if ((flags & ImGuiTableColumnFlags_PreferSortDescending) != 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; }\n        if ((flags & ImGuiTableColumnFlags_PreferSortAscending)  == 0 && (flags & ImGuiTableColumnFlags_NoSortAscending)  == 0) { mask |= 1 << ImGuiSortDirection_Ascending;  list |= ImGuiSortDirection_Ascending  << (count << 1); count++; }\n        if ((flags & ImGuiTableColumnFlags_PreferSortDescending) == 0 && (flags & ImGuiTableColumnFlags_NoSortDescending) == 0) { mask |= 1 << ImGuiSortDirection_Descending; list |= ImGuiSortDirection_Descending << (count << 1); count++; }\n        if ((table->Flags & ImGuiTableFlags_SortTristate) || count == 0) { mask |= 1 << ImGuiSortDirection_None; count++; }\n        column->SortDirectionsAvailList = (ImU8)list;\n        column->SortDirectionsAvailMask = (ImU8)mask;\n        column->SortDirectionsAvailCount = (ImU8)count;\n        ImGui::TableFixColumnSortDirection(table, column);\n    }\n}\n\n// Layout columns for the frame. This is in essence the followup to BeginTable() and this is our largest function.\n// Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() and other TableSetupXXXXX() functions to be called first.\n// FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for _WidthAuto columns.\n// Increase feedback side-effect with widgets relying on WorkRect.Max.x... Maybe provide a default distribution for _WidthAuto columns?\nvoid ImGui::TableUpdateLayout(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(table->IsLayoutLocked == false);\n\n    const ImGuiTableFlags table_sizing_policy = (table->Flags & ImGuiTableFlags_SizingMask_);\n    table->IsDefaultDisplayOrder = true;\n    table->ColumnsEnabledCount = 0;\n    ImBitArrayClearAllBits(table->EnabledMaskByIndex, table->ColumnsCount);\n    ImBitArrayClearAllBits(table->EnabledMaskByDisplayOrder, table->ColumnsCount);\n    table->LeftMostEnabledColumn = -1;\n    table->MinColumnWidth = ImMax(1.0f, g.Style.FramePadding.x * 1.0f); // g.Style.ColumnsMinSpacing; // FIXME-TABLE\n\n    // [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns.\n    // Process columns in their visible orders as we are building the Prev/Next indices.\n    int count_fixed = 0;                // Number of columns that have fixed sizing policies\n    int count_stretch = 0;              // Number of columns that have stretch sizing policies\n    int prev_visible_column_idx = -1;\n    bool has_auto_fit_request = false;\n    bool has_resizable = false;\n    float stretch_sum_width_auto = 0.0f;\n    float fixed_max_width_auto = 0.0f;\n    for (int order_n = 0; order_n < table->ColumnsCount; order_n++)\n    {\n        const int column_n = table->DisplayOrderToIndex[order_n];\n        if (column_n != order_n)\n            table->IsDefaultDisplayOrder = false;\n        ImGuiTableColumn* column = &table->Columns[column_n];\n\n        // Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame.\n        // It would easily work without but we're not ready to guarantee it since e.g. names need resubmission anyway.\n        // We take a slight shortcut but in theory we could be calling TableSetupColumn() here with dummy values, it should yield the same effect.\n        if (table->DeclColumnsCount <= column_n)\n        {\n            TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None);\n            column->NameOffset = -1;\n            column->UserID = 0;\n            column->InitStretchWeightOrWidth = -1.0f;\n        }\n\n        // Update Enabled state, mark settings and sort specs dirty\n        if (!(table->Flags & ImGuiTableFlags_Hideable) || (column->Flags & ImGuiTableColumnFlags_NoHide))\n            column->IsUserEnabledNextFrame = true;\n        if (column->IsUserEnabled != column->IsUserEnabledNextFrame)\n        {\n            column->IsUserEnabled = column->IsUserEnabledNextFrame;\n            table->IsSettingsDirty = true;\n        }\n        column->IsEnabled = column->IsUserEnabled && (column->Flags & ImGuiTableColumnFlags_Disabled) == 0;\n\n        if (column->SortOrder != -1 && !column->IsEnabled)\n            table->IsSortSpecsDirty = true;\n        if (column->SortOrder > 0 && !(table->Flags & ImGuiTableFlags_SortMulti))\n            table->IsSortSpecsDirty = true;\n\n        // Auto-fit unsized columns\n        const bool start_auto_fit = (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? (column->WidthRequest < 0.0f) : (column->StretchWeight < 0.0f);\n        if (start_auto_fit)\n            column->AutoFitQueue = column->CannotSkipItemsQueue = (1 << 3) - 1; // Fit for three frames\n\n        if (!column->IsEnabled)\n        {\n            column->IndexWithinEnabledSet = -1;\n            continue;\n        }\n\n        // Mark as enabled and link to previous/next enabled column\n        column->PrevEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx;\n        column->NextEnabledColumn = -1;\n        if (prev_visible_column_idx != -1)\n            table->Columns[prev_visible_column_idx].NextEnabledColumn = (ImGuiTableColumnIdx)column_n;\n        else\n            table->LeftMostEnabledColumn = (ImGuiTableColumnIdx)column_n;\n        column->IndexWithinEnabledSet = table->ColumnsEnabledCount++;\n        ImBitArraySetBit(table->EnabledMaskByIndex, column_n);\n        ImBitArraySetBit(table->EnabledMaskByDisplayOrder, column->DisplayOrder);\n        prev_visible_column_idx = column_n;\n        IM_ASSERT(column->IndexWithinEnabledSet <= column->DisplayOrder);\n\n        // Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)\n        // Combine width from regular rows + width from headers unless requested not to.\n        if (!column->IsPreserveWidthAuto)\n            column->WidthAuto = TableGetColumnWidthAuto(table, column);\n\n        // Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)\n        const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0;\n        if (column_is_resizable)\n            has_resizable = true;\n        if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f && !column_is_resizable)\n            column->WidthAuto = column->InitStretchWeightOrWidth;\n\n        if (column->AutoFitQueue != 0x00)\n            has_auto_fit_request = true;\n        if (column->Flags & ImGuiTableColumnFlags_WidthStretch)\n        {\n            stretch_sum_width_auto += column->WidthAuto;\n            count_stretch++;\n        }\n        else\n        {\n            fixed_max_width_auto = ImMax(fixed_max_width_auto, column->WidthAuto);\n            count_fixed++;\n        }\n    }\n    if ((table->Flags & ImGuiTableFlags_Sortable) && table->SortSpecsCount == 0 && !(table->Flags & ImGuiTableFlags_SortTristate))\n        table->IsSortSpecsDirty = true;\n    table->RightMostEnabledColumn = (ImGuiTableColumnIdx)prev_visible_column_idx;\n    IM_ASSERT(table->LeftMostEnabledColumn >= 0 && table->RightMostEnabledColumn >= 0);\n\n    // [Part 2] Disable child window clipping while fitting columns. This is not strictly necessary but makes it possible to avoid\n    // the column fitting having to wait until the first visible frame of the child container (may or not be a good thing). Also see #6510.\n    // FIXME-TABLE: for always auto-resizing columns may not want to do that all the time.\n    if (has_auto_fit_request && table->OuterWindow != table->InnerWindow)\n        table->InnerWindow->SkipItems = false;\n    if (has_auto_fit_request)\n        table->IsSettingsDirty = true;\n\n    // [Part 3] Fix column flags and record a few extra information.\n    float sum_width_requests = 0.0f;    // Sum of all width for fixed and auto-resize columns, excluding width contributed by Stretch columns but including spacing/padding.\n    float stretch_sum_weights = 0.0f;   // Sum of all weights for stretch columns.\n    table->LeftMostStretchedColumn = table->RightMostStretchedColumn = -1;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))\n            continue;\n        ImGuiTableColumn* column = &table->Columns[column_n];\n\n        const bool column_is_resizable = (column->Flags & ImGuiTableColumnFlags_NoResize) == 0;\n        if (column->Flags & ImGuiTableColumnFlags_WidthFixed)\n        {\n            // Apply same widths policy\n            float width_auto = column->WidthAuto;\n            if (table_sizing_policy == ImGuiTableFlags_SizingFixedSame && (column->AutoFitQueue != 0x00 || !column_is_resizable))\n                width_auto = fixed_max_width_auto;\n\n            // Apply automatic width\n            // Latch initial size for fixed columns and update it constantly for auto-resizing column (unless clipped!)\n            if (column->AutoFitQueue != 0x00)\n                column->WidthRequest = width_auto;\n            else if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !column_is_resizable && column->IsRequestOutput)\n                column->WidthRequest = width_auto;\n\n            // FIXME-TABLE: Increase minimum size during init frame to avoid biasing auto-fitting widgets\n            // (e.g. TextWrapped) too much. Otherwise what tends to happen is that TextWrapped would output a very\n            // large height (= first frame scrollbar display very off + clipper would skip lots of items).\n            // This is merely making the side-effect less extreme, but doesn't properly fixes it.\n            // FIXME: Move this to ->WidthGiven to avoid temporary lossyless?\n            // FIXME: This break IsPreserveWidthAuto from not flickering if the stored WidthAuto was smaller.\n            if (column->AutoFitQueue > 0x01 && table->IsInitializing && !column->IsPreserveWidthAuto)\n                column->WidthRequest = ImMax(column->WidthRequest, table->MinColumnWidth * 4.0f); // FIXME-TABLE: Another constant/scale?\n            sum_width_requests += column->WidthRequest;\n        }\n        else\n        {\n            // Initialize stretch weight\n            if (column->AutoFitQueue != 0x00 || column->StretchWeight < 0.0f || !column_is_resizable)\n            {\n                if (column->InitStretchWeightOrWidth > 0.0f)\n                    column->StretchWeight = column->InitStretchWeightOrWidth;\n                else if (table_sizing_policy == ImGuiTableFlags_SizingStretchProp)\n                    column->StretchWeight = (column->WidthAuto / stretch_sum_width_auto) * count_stretch;\n                else\n                    column->StretchWeight = 1.0f;\n            }\n\n            stretch_sum_weights += column->StretchWeight;\n            if (table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder > column->DisplayOrder)\n                table->LeftMostStretchedColumn = (ImGuiTableColumnIdx)column_n;\n            if (table->RightMostStretchedColumn == -1 || table->Columns[table->RightMostStretchedColumn].DisplayOrder < column->DisplayOrder)\n                table->RightMostStretchedColumn = (ImGuiTableColumnIdx)column_n;\n        }\n        column->IsPreserveWidthAuto = false;\n        sum_width_requests += table->CellPaddingX * 2.0f;\n    }\n    table->ColumnsEnabledFixedCount = (ImGuiTableColumnIdx)count_fixed;\n    table->ColumnsStretchSumWeights = stretch_sum_weights;\n\n    // [Part 4] Apply final widths based on requested widths\n    const ImRect work_rect = table->WorkRect;\n    const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);\n    const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synched tables with mismatching scrollbar state (#5920)\n    const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed);\n    const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests;\n    float width_remaining_for_stretched_columns = width_avail_for_stretched_columns;\n    table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))\n            continue;\n        ImGuiTableColumn* column = &table->Columns[column_n];\n\n        // Allocate width for stretched/weighted columns (StretchWeight gets converted into WidthRequest)\n        if (column->Flags & ImGuiTableColumnFlags_WidthStretch)\n        {\n            float weight_ratio = column->StretchWeight / stretch_sum_weights;\n            column->WidthRequest = IM_TRUNC(ImMax(width_avail_for_stretched_columns * weight_ratio, table->MinColumnWidth) + 0.01f);\n            width_remaining_for_stretched_columns -= column->WidthRequest;\n        }\n\n        // [Resize Rule 1] The right-most Visible column is not resizable if there is at least one Stretch column\n        // See additional comments in TableSetColumnWidth().\n        if (column->NextEnabledColumn == -1 && table->LeftMostStretchedColumn != -1)\n            column->Flags |= ImGuiTableColumnFlags_NoDirectResize_;\n\n        // Assign final width, record width in case we will need to shrink\n        column->WidthGiven = ImTrunc(ImMax(column->WidthRequest, table->MinColumnWidth));\n        table->ColumnsGivenWidth += column->WidthGiven;\n    }\n\n    // [Part 5] Redistribute stretch remainder width due to rounding (remainder width is < 1.0f * number of Stretch column).\n    // Using right-to-left distribution (more likely to match resizing cursor).\n    if (width_remaining_for_stretched_columns >= 1.0f && !(table->Flags & ImGuiTableFlags_PreciseWidths))\n        for (int order_n = table->ColumnsCount - 1; stretch_sum_weights > 0.0f && width_remaining_for_stretched_columns >= 1.0f && order_n >= 0; order_n--)\n        {\n            if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))\n                continue;\n            ImGuiTableColumn* column = &table->Columns[table->DisplayOrderToIndex[order_n]];\n            if (!(column->Flags & ImGuiTableColumnFlags_WidthStretch))\n                continue;\n            column->WidthRequest += 1.0f;\n            column->WidthGiven += 1.0f;\n            width_remaining_for_stretched_columns -= 1.0f;\n        }\n\n    // Determine if table is hovered which will be used to flag columns as hovered.\n    // - In principle we'd like to use the equivalent of IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),\n    //   but because our item is partially submitted at this point we use ItemHoverable() and a workaround (temporarily\n    //   clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem).\n    // - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop.\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    table_instance->HoveredRowLast = table_instance->HoveredRowNext;\n    table_instance->HoveredRowNext = -1;\n    table->HoveredColumnBody = table->HoveredColumnBorder = -1;\n    const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight));\n    const ImGuiID backup_active_id = g.ActiveId;\n    g.ActiveId = 0;\n    const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0, ImGuiItemFlags_None);\n    g.ActiveId = backup_active_id;\n\n    // Determine skewed MousePos.x to support angled headers.\n    float mouse_skewed_x = g.IO.MousePos.x;\n    if (table->AngledHeadersHeight > 0.0f)\n        if (g.IO.MousePos.y >= table->OuterRect.Min.y && g.IO.MousePos.y <= table->OuterRect.Min.y + table->AngledHeadersHeight)\n            mouse_skewed_x += ImTrunc((table->OuterRect.Min.y + table->AngledHeadersHeight - g.IO.MousePos.y) * table->AngledHeadersSlope);\n\n    // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column\n    // Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping.\n    int visible_n = 0;\n    bool has_at_least_one_column_requesting_output = false;\n    bool offset_x_frozen = (table->FreezeColumnsCount > 0);\n    float offset_x = ((table->FreezeColumnsCount > 0) ? table->OuterRect.Min.x : work_rect.Min.x) + table->OuterPaddingX - table->CellSpacingX1;\n    ImRect host_clip_rect = table->InnerClipRect;\n    //host_clip_rect.Max.x += table->CellPaddingX + table->CellSpacingX2;\n    ImBitArrayClearAllBits(table->VisibleMaskByIndex, table->ColumnsCount);\n    for (int order_n = 0; order_n < table->ColumnsCount; order_n++)\n    {\n        const int column_n = table->DisplayOrderToIndex[order_n];\n        ImGuiTableColumn* column = &table->Columns[column_n];\n\n        column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen\n\n        if (offset_x_frozen && table->FreezeColumnsCount == visible_n)\n        {\n            offset_x += work_rect.Min.x - table->OuterRect.Min.x;\n            offset_x_frozen = false;\n        }\n\n        // Clear status flags\n        column->Flags &= ~ImGuiTableColumnFlags_StatusMask_;\n\n        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))\n        {\n            // Hidden column: clear a few fields and we are done with it for the remainder of the function.\n            // We set a zero-width clip rect but set Min.y/Max.y properly to not interfere with the clipper.\n            column->MinX = column->MaxX = column->WorkMinX = column->ClipRect.Min.x = column->ClipRect.Max.x = offset_x;\n            column->WidthGiven = 0.0f;\n            column->ClipRect.Min.y = work_rect.Min.y;\n            column->ClipRect.Max.y = FLT_MAX;\n            column->ClipRect.ClipWithFull(host_clip_rect);\n            column->IsVisibleX = column->IsVisibleY = column->IsRequestOutput = false;\n            column->IsSkipItems = true;\n            column->ItemWidth = 1.0f;\n            continue;\n        }\n\n        // Detect hovered column\n        if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)\n            table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;\n\n        // Lock start position\n        column->MinX = offset_x;\n\n        // Lock width based on start position and minimum/maximum width for this position\n        float max_width = TableGetMaxColumnWidth(table, column_n);\n        column->WidthGiven = ImMin(column->WidthGiven, max_width);\n        column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth));\n        column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;\n\n        // Lock other positions\n        // - ClipRect.Min.x: Because merging draw commands doesn't compare min boundaries, we make ClipRect.Min.x match left bounds to be consistent regardless of merging.\n        // - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column.\n        // - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow.\n        // - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter.\n        column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1;\n        column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max\n        column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f);\n        column->ClipRect.Min.x = column->MinX;\n        column->ClipRect.Min.y = work_rect.Min.y;\n        column->ClipRect.Max.x = column->MaxX; //column->WorkMaxX;\n        column->ClipRect.Max.y = FLT_MAX;\n        column->ClipRect.ClipWithFull(host_clip_rect);\n\n        // Mark column as Clipped (not in sight)\n        // Note that scrolling tables (where inner_window != outer_window) handle Y clipped earlier in BeginTable() so IsVisibleY really only applies to non-scrolling tables.\n        // FIXME-TABLE: Because InnerClipRect.Max.y is conservatively ==outer_window->ClipRect.Max.y, we never can mark columns _Above_ the scroll line as not IsVisibleY.\n        // Taking advantage of LastOuterHeight would yield good results there...\n        // FIXME-TABLE: Y clipping is disabled because it effectively means not submitting will reduce contents width which is fed to outer_window->DC.CursorMaxPos.x,\n        // and this may be used (e.g. typically by outer_window using AlwaysAutoResize or outer_window's horizontal scrollbar, but could be something else).\n        // Possible solution to preserve last known content width for clipped column. Test 'table_reported_size' fails when enabling Y clipping and window is resized small.\n        column->IsVisibleX = (column->ClipRect.Max.x > column->ClipRect.Min.x);\n        column->IsVisibleY = true; // (column->ClipRect.Max.y > column->ClipRect.Min.y);\n        const bool is_visible = column->IsVisibleX; //&& column->IsVisibleY;\n        if (is_visible)\n            ImBitArraySetBit(table->VisibleMaskByIndex, column_n);\n\n        // Mark column as requesting output from user. Note that fixed + non-resizable sets are auto-fitting at all times and therefore always request output.\n        column->IsRequestOutput = is_visible || column->AutoFitQueue != 0 || column->CannotSkipItemsQueue != 0;\n\n        // Mark column as SkipItems (ignoring all items/layout)\n        // (table->HostSkipItems is a copy of inner_window->SkipItems before we cleared it above in Part 2)\n        column->IsSkipItems = !column->IsEnabled || table->HostSkipItems;\n        if (column->IsSkipItems)\n            IM_ASSERT(!is_visible);\n        if (column->IsRequestOutput && !column->IsSkipItems)\n            has_at_least_one_column_requesting_output = true;\n\n        // Update status flags\n        column->Flags |= ImGuiTableColumnFlags_IsEnabled;\n        if (is_visible)\n            column->Flags |= ImGuiTableColumnFlags_IsVisible;\n        if (column->SortOrder != -1)\n            column->Flags |= ImGuiTableColumnFlags_IsSorted;\n        if (table->HoveredColumnBody == column_n)\n            column->Flags |= ImGuiTableColumnFlags_IsHovered;\n\n        // Alignment\n        // FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in\n        // many cases (to be able to honor this we might be able to store a log of cells width, per row, for\n        // visible rows, but nav/programmatic scroll would have visible artifacts.)\n        //if (column->Flags & ImGuiTableColumnFlags_AlignRight)\n        //    column->WorkMinX = ImMax(column->WorkMinX, column->MaxX - column->ContentWidthRowsUnfrozen);\n        //else if (column->Flags & ImGuiTableColumnFlags_AlignCenter)\n        //    column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f);\n\n        // Reset content width variables\n        column->ContentMaxXFrozen = column->ContentMaxXUnfrozen = column->WorkMinX;\n        column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX;\n\n        // Don't decrement auto-fit counters until container window got a chance to submit its items\n        if (table->HostSkipItems == false)\n        {\n            column->AutoFitQueue >>= 1;\n            column->CannotSkipItemsQueue >>= 1;\n        }\n\n        if (visible_n < table->FreezeColumnsCount)\n            host_clip_rect.Min.x = ImClamp(column->MaxX + TABLE_BORDER_SIZE, host_clip_rect.Min.x, host_clip_rect.Max.x);\n\n        offset_x += column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;\n        visible_n++;\n    }\n\n    // In case the table is visible (e.g. decorations) but all columns clipped, we keep a column visible.\n    // Else if give no chance to a clipper-savy user to submit rows and therefore total contents height used by scrollbar.\n    if (has_at_least_one_column_requesting_output == false)\n    {\n        table->Columns[table->LeftMostEnabledColumn].IsRequestOutput = true;\n        table->Columns[table->LeftMostEnabledColumn].IsSkipItems = false;\n    }\n\n    // [Part 7] Detect/store when we are hovering the unused space after the right-most column (so e.g. context menus can react on it)\n    // Clear Resizable flag if none of our column are actually resizable (either via an explicit _NoResize flag, either\n    // because of using _WidthAuto/_WidthStretch). This will hide the resizing option from the context menu.\n    const float unused_x1 = ImMax(table->WorkRect.Min.x, table->Columns[table->RightMostEnabledColumn].ClipRect.Max.x);\n    if (is_hovering_table && table->HoveredColumnBody == -1)\n        if (mouse_skewed_x >= unused_x1)\n            table->HoveredColumnBody = (ImGuiTableColumnIdx)table->ColumnsCount;\n    if (has_resizable == false && (table->Flags & ImGuiTableFlags_Resizable))\n        table->Flags &= ~ImGuiTableFlags_Resizable;\n\n    table->IsActiveIdAliveBeforeTable = (g.ActiveIdIsAlive != 0);\n\n    // [Part 8] Lock actual OuterRect/WorkRect right-most position.\n    // This is done late to handle the case of fixed-columns tables not claiming more widths that they need.\n    // Because of this we are careful with uses of WorkRect and InnerClipRect before this point.\n    if (table->RightMostStretchedColumn != -1)\n        table->Flags &= ~ImGuiTableFlags_NoHostExtendX;\n    if (table->Flags & ImGuiTableFlags_NoHostExtendX)\n    {\n        table->OuterRect.Max.x = table->WorkRect.Max.x = unused_x1;\n        table->InnerClipRect.Max.x = ImMin(table->InnerClipRect.Max.x, unused_x1);\n    }\n    table->InnerWindow->ParentWorkRect = table->WorkRect;\n    table->BorderX1 = table->InnerClipRect.Min.x;\n    table->BorderX2 = table->InnerClipRect.Max.x;\n\n    // Setup window's WorkRect.Max.y for GetContentRegionAvail(). Other values will be updated in each TableBeginCell() call.\n    float window_content_max_y;\n    if (table->Flags & ImGuiTableFlags_NoHostExtendY)\n        window_content_max_y = table->OuterRect.Max.y;\n    else\n        window_content_max_y = ImMax(table->InnerWindow->ContentRegionRect.Max.y, (table->Flags & ImGuiTableFlags_ScrollY) ? 0.0f : table->OuterRect.Max.y);\n    table->InnerWindow->WorkRect.Max.y = ImClamp(window_content_max_y - g.Style.CellPadding.y, table->InnerWindow->WorkRect.Min.y, table->InnerWindow->WorkRect.Max.y);\n\n    // [Part 9] Allocate draw channels and setup background cliprect\n    TableSetupDrawChannels(table);\n\n    // [Part 10] Hit testing on borders\n    if (table->Flags & ImGuiTableFlags_Resizable)\n        TableUpdateBorders(table);\n    table_instance->LastTopHeadersRowHeight = 0.0f;\n    table->IsLayoutLocked = true;\n    table->IsUsingHeaders = false;\n\n    // Highlight header\n    table->HighlightColumnHeader = -1;\n    if (table->IsContextPopupOpen && table->ContextPopupColumn != -1 && table->InstanceInteracted == table->InstanceCurrent)\n        table->HighlightColumnHeader = table->ContextPopupColumn;\n    else if ((table->Flags & ImGuiTableFlags_HighlightHoveredColumn) && table->HoveredColumnBody != -1 && table->HoveredColumnBody != table->ColumnsCount && table->HoveredColumnBorder == -1)\n        if (g.ActiveId == 0 || (table->IsActiveIdInTable || g.DragDropActive))\n            table->HighlightColumnHeader = table->HoveredColumnBody;\n\n    // [Part 11] Default context menu\n    // - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().\n    // - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup().\n    // - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,\n    //   e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.\n    if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))\n    {\n        TableDrawDefaultContextMenu(table, table->Flags);\n        EndPopup();\n    }\n\n    // [Part 12] Sanitize and build sort specs before we have a chance to use them for display.\n    // This path will only be exercised when sort specs are modified before header rows (e.g. init or visibility change)\n    if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable))\n        TableSortSpecsBuild(table);\n\n    // [Part 13] Setup inner window decoration size (for scrolling / nav tracking to properly take account of frozen rows/columns)\n    if (table->FreezeColumnsRequest > 0)\n        table->InnerWindow->DecoInnerSizeX1 = table->Columns[table->DisplayOrderToIndex[table->FreezeColumnsRequest - 1]].MaxX - table->OuterRect.Min.x;\n    if (table->FreezeRowsRequest > 0)\n        table->InnerWindow->DecoInnerSizeY1 = table_instance->LastFrozenHeight;\n    table_instance->LastFrozenHeight = 0.0f;\n\n    // Initial state\n    ImGuiWindow* inner_window = table->InnerWindow;\n    if (table->Flags & ImGuiTableFlags_NoClip)\n        table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);\n    else\n        inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false);\n}\n\n// Process hit-testing on resizing borders. Actual size change will be applied in EndTable()\n// - Set table->HoveredColumnBorder with a short delay/timer to reduce visual feedback noise.\nvoid ImGui::TableUpdateBorders(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(table->Flags & ImGuiTableFlags_Resizable);\n\n    // At this point OuterRect height may be zero or under actual final height, so we rely on temporal coherency and\n    // use the final height from last frame. Because this is only affecting _interaction_ with columns, it is not\n    // really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height).\n    // Actual columns highlight/render will be performed in EndTable() and not be affected.\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS;\n    const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight;\n    const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight);\n    const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight;\n\n    for (int order_n = 0; order_n < table->ColumnsCount; order_n++)\n    {\n        if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))\n            continue;\n\n        const int column_n = table->DisplayOrderToIndex[order_n];\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_))\n            continue;\n\n        // ImGuiTableFlags_NoBordersInBodyUntilResize will be honored in TableDrawBorders()\n        const float border_y2_hit = (table->Flags & ImGuiTableFlags_NoBordersInBody) ? hit_y2_head : hit_y2_body;\n        if ((table->Flags & ImGuiTableFlags_NoBordersInBody) && table->IsUsingHeaders == false)\n            continue;\n\n        if (!column->IsVisibleX && table->LastResizedColumn != column_n)\n            continue;\n\n        ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent);\n        ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit);\n        ItemAdd(hit_rect, column_id, NULL, ImGuiItemFlags_NoNav);\n        //GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100));\n\n        bool hovered = false, held = false;\n        bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus);\n        if (pressed && IsMouseDoubleClicked(0))\n        {\n            TableSetColumnWidthAutoSingle(table, column_n);\n            ClearActiveID();\n            held = false;\n        }\n        if (held)\n        {\n            if (table->LastResizedColumn == -1)\n                table->ResizeLockMinContentsX2 = table->RightMostEnabledColumn != -1 ? table->Columns[table->RightMostEnabledColumn].MaxX : -FLT_MAX;\n            table->ResizedColumn = (ImGuiTableColumnIdx)column_n;\n            table->InstanceInteracted = table->InstanceCurrent;\n        }\n        if ((hovered && g.HoveredIdTimer > TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER) || held)\n        {\n            table->HoveredColumnBorder = (ImGuiTableColumnIdx)column_n;\n            SetMouseCursor(ImGuiMouseCursor_ResizeEW);\n        }\n    }\n}\n\nvoid    ImGui::EndTable()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT(table != NULL && \"Only call EndTable() if BeginTable() returns true!\");\n\n    // This assert would be very useful to catch a common error... unfortunately it would probably trigger in some\n    // cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border)\n    //IM_ASSERT(table->IsLayoutLocked && \"Table unused: never called TableNextRow(), is that the intent?\");\n\n    // If the user never got to call TableNextRow() or TableNextColumn(), we call layout ourselves to ensure all our\n    // code paths are consistent (instead of just hoping that TableBegin/TableEnd will work), get borders drawn, etc.\n    if (!table->IsLayoutLocked)\n        TableUpdateLayout(table);\n\n    const ImGuiTableFlags flags = table->Flags;\n    ImGuiWindow* inner_window = table->InnerWindow;\n    ImGuiWindow* outer_window = table->OuterWindow;\n    ImGuiTableTempData* temp_data = table->TempData;\n    IM_ASSERT(inner_window == g.CurrentWindow);\n    IM_ASSERT(outer_window == inner_window || outer_window == inner_window->ParentWindow);\n\n    if (table->IsInsideRow)\n        TableEndRow(table);\n\n    // Context menu in columns body\n    if (flags & ImGuiTableFlags_ContextMenuInBody)\n        if (table->HoveredColumnBody != -1 && !IsAnyItemHovered() && IsMouseReleased(ImGuiMouseButton_Right))\n            TableOpenContextMenu((int)table->HoveredColumnBody);\n\n    // Finalize table height\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    inner_window->DC.PrevLineSize = temp_data->HostBackupPrevLineSize;\n    inner_window->DC.CurrLineSize = temp_data->HostBackupCurrLineSize;\n    inner_window->DC.CursorMaxPos = temp_data->HostBackupCursorMaxPos;\n    const float inner_content_max_y = table->RowPosY2;\n    IM_ASSERT(table->RowPosY2 == inner_window->DC.CursorPos.y);\n    if (inner_window != outer_window)\n        inner_window->DC.CursorMaxPos.y = inner_content_max_y;\n    else if (!(flags & ImGuiTableFlags_NoHostExtendY))\n        table->OuterRect.Max.y = table->InnerRect.Max.y = ImMax(table->OuterRect.Max.y, inner_content_max_y); // Patch OuterRect/InnerRect height\n    table->WorkRect.Max.y = ImMax(table->WorkRect.Max.y, table->OuterRect.Max.y);\n    table_instance->LastOuterHeight = table->OuterRect.GetHeight();\n\n    // Setup inner scrolling range\n    // FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y,\n    // but since the later is likely to be impossible to do we'd rather update both axises together.\n    if (table->Flags & ImGuiTableFlags_ScrollX)\n    {\n        const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;\n        float max_pos_x = table->InnerWindow->DC.CursorMaxPos.x;\n        if (table->RightMostEnabledColumn != -1)\n            max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border);\n        if (table->ResizedColumn != -1)\n            max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2);\n        table->InnerWindow->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledheadersExtraWidth;\n    }\n\n    // Pop clipping rect\n    if (!(flags & ImGuiTableFlags_NoClip))\n        inner_window->DrawList->PopClipRect();\n    inner_window->ClipRect = inner_window->DrawList->_ClipRectStack.back();\n\n    // Draw borders\n    if ((flags & ImGuiTableFlags_Borders) != 0)\n        TableDrawBorders(table);\n\n#if 0\n    // Strip out dummy channel draw calls\n    // We have no way to prevent user submitting direct ImDrawList calls into a hidden column (but ImGui:: calls will be clipped out)\n    // Pros: remove draw calls which will have no effect. since they'll have zero-size cliprect they may be early out anyway.\n    // Cons: making it harder for users watching metrics/debugger to spot the wasted vertices.\n    if (table->DummyDrawChannel != (ImGuiTableColumnIdx)-1)\n    {\n        ImDrawChannel* dummy_channel = &table->DrawSplitter._Channels[table->DummyDrawChannel];\n        dummy_channel->_CmdBuffer.resize(0);\n        dummy_channel->_IdxBuffer.resize(0);\n    }\n#endif\n\n    // Flatten channels and merge draw calls\n    ImDrawListSplitter* splitter = table->DrawSplitter;\n    splitter->SetCurrentChannel(inner_window->DrawList, 0);\n    if ((table->Flags & ImGuiTableFlags_NoClip) == 0)\n        TableMergeDrawChannels(table);\n    splitter->Merge(inner_window->DrawList);\n\n    // Update ColumnsAutoFitWidth to get us ahead for host using our size to auto-resize without waiting for next BeginTable()\n    float auto_fit_width_for_fixed = 0.0f;\n    float auto_fit_width_for_stretched = 0.0f;\n    float auto_fit_width_for_stretched_min = 0.0f;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))\n        {\n            ImGuiTableColumn* column = &table->Columns[column_n];\n            float column_width_request = ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && !(column->Flags & ImGuiTableColumnFlags_NoResize)) ? column->WidthRequest : TableGetColumnWidthAuto(table, column);\n            if (column->Flags & ImGuiTableColumnFlags_WidthFixed)\n                auto_fit_width_for_fixed += column_width_request;\n            else\n                auto_fit_width_for_stretched += column_width_request;\n            if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) && (column->Flags & ImGuiTableColumnFlags_NoResize) != 0)\n                auto_fit_width_for_stretched_min = ImMax(auto_fit_width_for_stretched_min, column_width_request / (column->StretchWeight / table->ColumnsStretchSumWeights));\n        }\n    const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);\n    table->ColumnsAutoFitWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount + auto_fit_width_for_fixed + ImMax(auto_fit_width_for_stretched, auto_fit_width_for_stretched_min);\n\n    // Update scroll\n    if ((table->Flags & ImGuiTableFlags_ScrollX) == 0 && inner_window != outer_window)\n    {\n        inner_window->Scroll.x = 0.0f;\n    }\n    else if (table->LastResizedColumn != -1 && table->ResizedColumn == -1 && inner_window->ScrollbarX && table->InstanceInteracted == table->InstanceCurrent)\n    {\n        // When releasing a column being resized, scroll to keep the resulting column in sight\n        const float neighbor_width_to_keep_visible = table->MinColumnWidth + table->CellPaddingX * 2.0f;\n        ImGuiTableColumn* column = &table->Columns[table->LastResizedColumn];\n        if (column->MaxX < table->InnerClipRect.Min.x)\n            SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x - neighbor_width_to_keep_visible, 1.0f);\n        else if (column->MaxX > table->InnerClipRect.Max.x)\n            SetScrollFromPosX(inner_window, column->MaxX - inner_window->Pos.x + neighbor_width_to_keep_visible, 1.0f);\n    }\n\n    // Apply resizing/dragging at the end of the frame\n    if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted)\n    {\n        ImGuiTableColumn* column = &table->Columns[table->ResizedColumn];\n        const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + TABLE_RESIZE_SEPARATOR_HALF_THICKNESS);\n        const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f);\n        table->ResizedColumnNextWidth = new_width;\n    }\n\n    table->IsActiveIdInTable = (g.ActiveIdIsAlive != 0 && table->IsActiveIdAliveBeforeTable == false);\n\n    // Pop from id stack\n    IM_ASSERT_USER_ERROR(inner_window->IDStack.back() == table_instance->TableInstanceID, \"Mismatching PushID/PopID!\");\n    IM_ASSERT_USER_ERROR(outer_window->DC.ItemWidthStack.Size >= temp_data->HostBackupItemWidthStackSize, \"Too many PopItemWidth!\");\n    if (table->InstanceCurrent > 0)\n        PopID();\n    PopID();\n\n    // Restore window data that we modified\n    const ImVec2 backup_outer_max_pos = outer_window->DC.CursorMaxPos;\n    inner_window->WorkRect = temp_data->HostBackupWorkRect;\n    inner_window->ParentWorkRect = temp_data->HostBackupParentWorkRect;\n    inner_window->SkipItems = table->HostSkipItems;\n    outer_window->DC.CursorPos = table->OuterRect.Min;\n    outer_window->DC.ItemWidth = temp_data->HostBackupItemWidth;\n    outer_window->DC.ItemWidthStack.Size = temp_data->HostBackupItemWidthStackSize;\n    outer_window->DC.ColumnsOffset = temp_data->HostBackupColumnsOffset;\n\n    // Layout in outer window\n    // (FIXME: To allow auto-fit and allow desirable effect of SameLine() we dissociate 'used' vs 'ideal' size by overriding\n    // CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414)\n    if (inner_window != outer_window)\n    {\n        EndChild();\n    }\n    else\n    {\n        ItemSize(table->OuterRect.GetSize());\n        ItemAdd(table->OuterRect, 0);\n    }\n\n    // Override declared contents width/height to enable auto-resize while not needlessly adding a scrollbar\n    if (table->Flags & ImGuiTableFlags_NoHostExtendX)\n    {\n        // FIXME-TABLE: Could we remove this section?\n        // ColumnsAutoFitWidth may be one frame ahead here since for Fixed+NoResize is calculated from latest contents\n        IM_ASSERT((table->Flags & ImGuiTableFlags_ScrollX) == 0);\n        outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth);\n    }\n    else if (temp_data->UserOuterSize.x <= 0.0f)\n    {\n        const float decoration_size = table->TempData->AngledheadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f);\n        outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x);\n        outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth));\n    }\n    else\n    {\n        outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, table->OuterRect.Max.x);\n    }\n    if (temp_data->UserOuterSize.y <= 0.0f)\n    {\n        const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.y : 0.0f;\n        outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y);\n        outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y));\n    }\n    else\n    {\n        // OuterRect.Max.y may already have been pushed downward from the initial value (unless ImGuiTableFlags_NoHostExtendY is set)\n        outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, table->OuterRect.Max.y);\n    }\n\n    // Save settings\n    if (table->IsSettingsDirty)\n        TableSaveSettings(table);\n    table->IsInitializing = false;\n\n    // Clear or restore current table, if any\n    IM_ASSERT(g.CurrentWindow == outer_window && g.CurrentTable == table);\n    IM_ASSERT(g.TablesTempDataStacked > 0);\n    temp_data = (--g.TablesTempDataStacked > 0) ? &g.TablesTempData[g.TablesTempDataStacked - 1] : NULL;\n    g.CurrentTable = temp_data ? g.Tables.GetByIndex(temp_data->TableIndex) : NULL;\n    if (g.CurrentTable)\n    {\n        g.CurrentTable->TempData = temp_data;\n        g.CurrentTable->DrawSplitter = &temp_data->DrawSplitter;\n    }\n    outer_window->DC.CurrentTableIdx = g.CurrentTable ? g.Tables.GetIndex(g.CurrentTable) : -1;\n    NavUpdateCurrentWindowIsScrollPushableX();\n}\n\n// See \"COLUMNS SIZING POLICIES\" comments at the top of this file\n// If (init_width_or_weight <= 0.0f) it is ignored\nvoid ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT(table != NULL && \"Need to call TableSetupColumn() after BeginTable()!\");\n    IM_ASSERT(table->IsLayoutLocked == false && \"Need to call call TableSetupColumn() before first row!\");\n    IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && \"Illegal to pass StatusMask values to TableSetupColumn()\");\n    if (table->DeclColumnsCount >= table->ColumnsCount)\n    {\n        IM_ASSERT_USER_ERROR(table->DeclColumnsCount < table->ColumnsCount, \"Called TableSetupColumn() too many times!\");\n        return;\n    }\n\n    ImGuiTableColumn* column = &table->Columns[table->DeclColumnsCount];\n    table->DeclColumnsCount++;\n\n    // Assert when passing a width or weight if policy is entirely left to default, to avoid storing width into weight and vice-versa.\n    // Give a grace to users of ImGuiTableFlags_ScrollX.\n    if (table->IsDefaultSizingPolicy && (flags & ImGuiTableColumnFlags_WidthMask_) == 0 && (flags & ImGuiTableFlags_ScrollX) == 0)\n        IM_ASSERT(init_width_or_weight <= 0.0f && \"Can only specify width/weight if sizing policy is set explicitly in either Table or Column.\");\n\n    // When passing a width automatically enforce WidthFixed policy\n    // (whereas TableSetupColumnFlags would default to WidthAuto if table is not Resizable)\n    if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f)\n        if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)\n            flags |= ImGuiTableColumnFlags_WidthFixed;\n    if (flags & ImGuiTableColumnFlags_AngledHeader)\n    {\n        flags |= ImGuiTableColumnFlags_NoHeaderLabel;\n        table->AngledHeadersCount++;\n    }\n\n    TableSetupColumnFlags(table, column, flags);\n    column->UserID = user_id;\n    flags = column->Flags;\n\n    // Initialize defaults\n    column->InitStretchWeightOrWidth = init_width_or_weight;\n    if (table->IsInitializing)\n    {\n        // Init width or weight\n        if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f)\n        {\n            if ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f)\n                column->WidthRequest = init_width_or_weight;\n            if (flags & ImGuiTableColumnFlags_WidthStretch)\n                column->StretchWeight = (init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;\n\n            // Disable auto-fit if an explicit width/weight has been specified\n            if (init_width_or_weight > 0.0f)\n                column->AutoFitQueue = 0x00;\n        }\n\n        // Init default visibility/sort state\n        if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0)\n            column->IsUserEnabled = column->IsUserEnabledNextFrame = false;\n        if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0)\n        {\n            column->SortOrder = 0; // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.\n            column->SortDirection = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending);\n        }\n    }\n\n    // Store name (append with zero-terminator in contiguous buffer)\n    column->NameOffset = -1;\n    if (label != NULL && label[0] != 0)\n    {\n        column->NameOffset = (ImS16)table->ColumnsNames.size();\n        table->ColumnsNames.append(label, label + strlen(label) + 1);\n    }\n}\n\n// [Public]\nvoid ImGui::TableSetupScrollFreeze(int columns, int rows)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT(table != NULL && \"Need to call TableSetupColumn() after BeginTable()!\");\n    IM_ASSERT(table->IsLayoutLocked == false && \"Need to call TableSetupColumn() before first row!\");\n    IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS);\n    IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit\n\n    table->FreezeColumnsRequest = (table->Flags & ImGuiTableFlags_ScrollX) ? (ImGuiTableColumnIdx)ImMin(columns, table->ColumnsCount) : 0;\n    table->FreezeColumnsCount = (table->InnerWindow->Scroll.x != 0.0f) ? table->FreezeColumnsRequest : 0;\n    table->FreezeRowsRequest = (table->Flags & ImGuiTableFlags_ScrollY) ? (ImGuiTableColumnIdx)rows : 0;\n    table->FreezeRowsCount = (table->InnerWindow->Scroll.y != 0.0f) ? table->FreezeRowsRequest : 0;\n    table->IsUnfrozenRows = (table->FreezeRowsCount == 0); // Make sure this is set before TableUpdateLayout() so ImGuiListClipper can benefit from it.b\n\n    // Ensure frozen columns are ordered in their section. We still allow multiple frozen columns to be reordered.\n    // FIXME-TABLE: This work for preserving 2143 into 21|43. How about 4321 turning into 21|43? (preserve relative order in each section)\n    for (int column_n = 0; column_n < table->FreezeColumnsRequest; column_n++)\n    {\n        int order_n = table->DisplayOrderToIndex[column_n];\n        if (order_n != column_n && order_n >= table->FreezeColumnsRequest)\n        {\n            ImSwap(table->Columns[table->DisplayOrderToIndex[order_n]].DisplayOrder, table->Columns[table->DisplayOrderToIndex[column_n]].DisplayOrder);\n            ImSwap(table->DisplayOrderToIndex[order_n], table->DisplayOrderToIndex[column_n]);\n        }\n    }\n}\n\n//-----------------------------------------------------------------------------\n// [SECTION] Tables: Simple accessors\n//-----------------------------------------------------------------------------\n// - TableGetColumnCount()\n// - TableGetColumnName()\n// - TableGetColumnName() [Internal]\n// - TableSetColumnEnabled()\n// - TableGetColumnFlags()\n// - TableGetCellBgRect() [Internal]\n// - TableGetColumnResizeID() [Internal]\n// - TableGetHoveredColumn() [Internal]\n// - TableGetHoveredRow() [Internal]\n// - TableSetBgColor()\n//-----------------------------------------------------------------------------\n\nint ImGui::TableGetColumnCount()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    return table ? table->ColumnsCount : 0;\n}\n\nconst char* ImGui::TableGetColumnName(int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return NULL;\n    if (column_n < 0)\n        column_n = table->CurrentColumn;\n    return TableGetColumnName(table, column_n);\n}\n\nconst char* ImGui::TableGetColumnName(const ImGuiTable* table, int column_n)\n{\n    if (table->IsLayoutLocked == false && column_n >= table->DeclColumnsCount)\n        return \"\"; // NameOffset is invalid at this point\n    const ImGuiTableColumn* column = &table->Columns[column_n];\n    if (column->NameOffset == -1)\n        return \"\";\n    return &table->ColumnsNames.Buf[column->NameOffset];\n}\n\n// Change user accessible enabled/disabled state of a column (often perceived as \"showing/hiding\" from users point of view)\n// Note that end-user can use the context menu to change this themselves (right-click in headers, or right-click in columns body with ImGuiTableFlags_ContextMenuInBody)\n// - Require table to have the ImGuiTableFlags_Hideable flag because we are manipulating user accessible state.\n// - Request will be applied during next layout, which happens on the first call to TableNextRow() after BeginTable().\n// - For the getter you can test (TableGetColumnFlags() & ImGuiTableColumnFlags_IsEnabled) != 0.\n// - Alternative: the ImGuiTableColumnFlags_Disabled is an overriding/master disable flag which will also hide the column from context menu.\nvoid ImGui::TableSetColumnEnabled(int column_n, bool enabled)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT(table != NULL);\n    if (!table)\n        return;\n    IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above\n    if (column_n < 0)\n        column_n = table->CurrentColumn;\n    IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);\n    ImGuiTableColumn* column = &table->Columns[column_n];\n    column->IsUserEnabledNextFrame = enabled;\n}\n\n// We allow querying for an extra column in order to poll the IsHovered state of the right-most section\nImGuiTableColumnFlags ImGui::TableGetColumnFlags(int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return ImGuiTableColumnFlags_None;\n    if (column_n < 0)\n        column_n = table->CurrentColumn;\n    if (column_n == table->ColumnsCount)\n        return (table->HoveredColumnBody == column_n) ? ImGuiTableColumnFlags_IsHovered : ImGuiTableColumnFlags_None;\n    return table->Columns[column_n].Flags;\n}\n\n// Return the cell rectangle based on currently known height.\n// - Important: we generally don't know our row height until the end of the row, so Max.y will be incorrect in many situations.\n//   The only case where this is correct is if we provided a min_row_height to TableNextRow() and don't go below it, or in TableEndRow() when we locked that height.\n// - Important: if ImGuiTableFlags_PadOuterX is set but ImGuiTableFlags_PadInnerX is not set, the outer-most left and right\n//   columns report a small offset so their CellBgRect can extend up to the outer border.\n//   FIXME: But the rendering code in TableEndRow() nullifies that with clamping required for scrolling.\nImRect ImGui::TableGetCellBgRect(const ImGuiTable* table, int column_n)\n{\n    const ImGuiTableColumn* column = &table->Columns[column_n];\n    float x1 = column->MinX;\n    float x2 = column->MaxX;\n    //if (column->PrevEnabledColumn == -1)\n    //    x1 -= table->OuterPaddingX;\n    //if (column->NextEnabledColumn == -1)\n    //    x2 += table->OuterPaddingX;\n    x1 = ImMax(x1, table->WorkRect.Min.x);\n    x2 = ImMin(x2, table->WorkRect.Max.x);\n    return ImRect(x1, table->RowPosY1, x2, table->RowPosY2);\n}\n\n// Return the resizing ID for the right-side of the given column.\nImGuiID ImGui::TableGetColumnResizeID(ImGuiTable* table, int column_n, int instance_no)\n{\n    IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);\n    ImGuiID instance_id = TableGetInstanceID(table, instance_no);\n    return instance_id + 1 + column_n; // FIXME: #6140: still not ideal\n}\n\n// Return -1 when table is not hovered. return columns_count if hovering the unused space at the right of the right-most visible column.\nint ImGui::TableGetHoveredColumn()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return -1;\n    return (int)table->HoveredColumnBody;\n}\n\n// Return -1 when table is not hovered. Return maxrow+1 if in table but below last submitted row.\n// *IMPORTANT* Unlike TableGetHoveredColumn(), this has a one frame latency in updating the value.\n// This difference with is the reason why this is not public yet.\nint ImGui::TableGetHoveredRow()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return -1;\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    return (int)table_instance->HoveredRowLast;\n}\n\nvoid ImGui::TableSetBgColor(ImGuiTableBgTarget target, ImU32 color, int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT(target != ImGuiTableBgTarget_None);\n\n    if (color == IM_COL32_DISABLE)\n        color = 0;\n\n    // We cannot draw neither the cell or row background immediately as we don't know the row height at this point in time.\n    switch (target)\n    {\n    case ImGuiTableBgTarget_CellBg:\n    {\n        if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard\n            return;\n        if (column_n == -1)\n            column_n = table->CurrentColumn;\n        if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n))\n            return;\n        if (table->RowCellDataCurrent < 0 || table->RowCellData[table->RowCellDataCurrent].Column != column_n)\n            table->RowCellDataCurrent++;\n        ImGuiTableCellData* cell_data = &table->RowCellData[table->RowCellDataCurrent];\n        cell_data->BgColor = color;\n        cell_data->Column = (ImGuiTableColumnIdx)column_n;\n        break;\n    }\n    case ImGuiTableBgTarget_RowBg0:\n    case ImGuiTableBgTarget_RowBg1:\n    {\n        if (table->RowPosY1 > table->InnerClipRect.Max.y) // Discard\n            return;\n        IM_ASSERT(column_n == -1);\n        int bg_idx = (target == ImGuiTableBgTarget_RowBg1) ? 1 : 0;\n        table->RowBgColor[bg_idx] = color;\n        break;\n    }\n    default:\n        IM_ASSERT(0);\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Row changes\n//-------------------------------------------------------------------------\n// - TableGetRowIndex()\n// - TableNextRow()\n// - TableBeginRow() [Internal]\n// - TableEndRow() [Internal]\n//-------------------------------------------------------------------------\n\n// [Public] Note: for row coloring we use ->RowBgColorCounter which is the same value without counting header rows\nint ImGui::TableGetRowIndex()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return 0;\n    return table->CurrentRow;\n}\n\n// [Public] Starts into the first cell of a new row\nvoid ImGui::TableNextRow(ImGuiTableRowFlags row_flags, float row_min_height)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n\n    if (!table->IsLayoutLocked)\n        TableUpdateLayout(table);\n    if (table->IsInsideRow)\n        TableEndRow(table);\n\n    table->LastRowFlags = table->RowFlags;\n    table->RowFlags = row_flags;\n    table->RowCellPaddingY = g.Style.CellPadding.y;\n    table->RowMinHeight = row_min_height;\n    TableBeginRow(table);\n\n    // We honor min_row_height requested by user, but cannot guarantee per-row maximum height,\n    // because that would essentially require a unique clipping rectangle per-cell.\n    table->RowPosY2 += table->RowCellPaddingY * 2.0f;\n    table->RowPosY2 = ImMax(table->RowPosY2, table->RowPosY1 + row_min_height);\n\n    // Disable output until user calls TableNextColumn()\n    table->InnerWindow->SkipItems = true;\n}\n\n// [Internal] Only called by TableNextRow()\nvoid ImGui::TableBeginRow(ImGuiTable* table)\n{\n    ImGuiWindow* window = table->InnerWindow;\n    IM_ASSERT(!table->IsInsideRow);\n\n    // New row\n    table->CurrentRow++;\n    table->CurrentColumn = -1;\n    table->RowBgColor[0] = table->RowBgColor[1] = IM_COL32_DISABLE;\n    table->RowCellDataCurrent = -1;\n    table->IsInsideRow = true;\n\n    // Begin frozen rows\n    float next_y1 = table->RowPosY2;\n    if (table->CurrentRow == 0 && table->FreezeRowsCount > 0)\n        next_y1 = window->DC.CursorPos.y = table->OuterRect.Min.y;\n\n    table->RowPosY1 = table->RowPosY2 = next_y1;\n    table->RowTextBaseline = 0.0f;\n    table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent\n\n    window->DC.PrevLineTextBaseOffset = 0.0f;\n    window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + table->RowCellPaddingY); // This allows users to call SameLine() to share LineSize between columns.\n    window->DC.PrevLineSize = window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); // This allows users to call SameLine() to share LineSize between columns, and to call it from first column too.\n    window->DC.IsSameLine = window->DC.IsSetPos = false;\n    window->DC.CursorMaxPos.y = next_y1;\n\n    // Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging.\n    if (table->RowFlags & ImGuiTableRowFlags_Headers)\n    {\n        TableSetBgColor(ImGuiTableBgTarget_RowBg0, GetColorU32(ImGuiCol_TableHeaderBg));\n        if (table->CurrentRow == 0)\n            table->IsUsingHeaders = true;\n    }\n}\n\n// [Internal] Called by TableNextRow()\nvoid ImGui::TableEndRow(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(window == table->InnerWindow);\n    IM_ASSERT(table->IsInsideRow);\n\n    if (table->CurrentColumn != -1)\n        TableEndCell(table);\n\n    // Logging\n    if (g.LogEnabled)\n        LogRenderedText(NULL, \"|\");\n\n    // Position cursor at the bottom of our row so it can be used for e.g. clipping calculation. However it is\n    // likely that the next call to TableBeginCell() will reposition the cursor to take account of vertical padding.\n    window->DC.CursorPos.y = table->RowPosY2;\n\n    // Row background fill\n    const float bg_y1 = table->RowPosY1;\n    const float bg_y2 = table->RowPosY2;\n    const bool unfreeze_rows_actual = (table->CurrentRow + 1 == table->FreezeRowsCount);\n    const bool unfreeze_rows_request = (table->CurrentRow + 1 == table->FreezeRowsRequest);\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    if ((table->RowFlags & ImGuiTableRowFlags_Headers) && (table->CurrentRow == 0 || (table->LastRowFlags & ImGuiTableRowFlags_Headers)))\n        table_instance->LastTopHeadersRowHeight += bg_y2 - bg_y1;\n\n    const bool is_visible = (bg_y2 >= table->InnerClipRect.Min.y && bg_y1 <= table->InnerClipRect.Max.y);\n    if (is_visible)\n    {\n        // Update data for TableGetHoveredRow()\n        if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2)\n            table_instance->HoveredRowNext = table->CurrentRow;\n\n        // Decide of background color for the row\n        ImU32 bg_col0 = 0;\n        ImU32 bg_col1 = 0;\n        if (table->RowBgColor[0] != IM_COL32_DISABLE)\n            bg_col0 = table->RowBgColor[0];\n        else if (table->Flags & ImGuiTableFlags_RowBg)\n            bg_col0 = GetColorU32((table->RowBgColorCounter & 1) ? ImGuiCol_TableRowBgAlt : ImGuiCol_TableRowBg);\n        if (table->RowBgColor[1] != IM_COL32_DISABLE)\n            bg_col1 = table->RowBgColor[1];\n\n        // Decide of top border color\n        ImU32 top_border_col = 0;\n        const float border_size = TABLE_BORDER_SIZE;\n        if (table->CurrentRow > 0 && (table->Flags & ImGuiTableFlags_BordersInnerH))\n            top_border_col = (table->LastRowFlags & ImGuiTableRowFlags_Headers) ? table->BorderColorStrong : table->BorderColorLight;\n\n        const bool draw_cell_bg_color = table->RowCellDataCurrent >= 0;\n        const bool draw_strong_bottom_border = unfreeze_rows_actual;\n        if ((bg_col0 | bg_col1 | top_border_col) != 0 || draw_strong_bottom_border || draw_cell_bg_color)\n        {\n            // In theory we could call SetWindowClipRectBeforeSetChannel() but since we know TableEndRow() is\n            // always followed by a change of clipping rectangle we perform the smallest overwrite possible here.\n            if ((table->Flags & ImGuiTableFlags_NoClip) == 0)\n                window->DrawList->_CmdHeader.ClipRect = table->Bg0ClipRectForDrawCmd.ToVec4();\n            table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_BG0);\n        }\n\n        // Draw row background\n        // We soft/cpu clip this so all backgrounds and borders can share the same clipping rectangle\n        if (bg_col0 || bg_col1)\n        {\n            ImRect row_rect(table->WorkRect.Min.x, bg_y1, table->WorkRect.Max.x, bg_y2);\n            row_rect.ClipWith(table->BgClipRect);\n            if (bg_col0 != 0 && row_rect.Min.y < row_rect.Max.y)\n                window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col0);\n            if (bg_col1 != 0 && row_rect.Min.y < row_rect.Max.y)\n                window->DrawList->AddRectFilled(row_rect.Min, row_rect.Max, bg_col1);\n        }\n\n        // Draw cell background color\n        if (draw_cell_bg_color)\n        {\n            ImGuiTableCellData* cell_data_end = &table->RowCellData[table->RowCellDataCurrent];\n            for (ImGuiTableCellData* cell_data = &table->RowCellData[0]; cell_data <= cell_data_end; cell_data++)\n            {\n                // As we render the BG here we need to clip things (for layout we would not)\n                // FIXME: This cancels the OuterPadding addition done by TableGetCellBgRect(), need to keep it while rendering correctly while scrolling.\n                const ImGuiTableColumn* column = &table->Columns[cell_data->Column];\n                ImRect cell_bg_rect = TableGetCellBgRect(table, cell_data->Column);\n                cell_bg_rect.ClipWith(table->BgClipRect);\n                cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x);     // So that first column after frozen one gets clipped when scrolling\n                cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX);\n                window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor);\n            }\n        }\n\n        // Draw top border\n        if (top_border_col && bg_y1 >= table->BgClipRect.Min.y && bg_y1 < table->BgClipRect.Max.y)\n            window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y1), ImVec2(table->BorderX2, bg_y1), top_border_col, border_size);\n\n        // Draw bottom border at the row unfreezing mark (always strong)\n        if (draw_strong_bottom_border && bg_y2 >= table->BgClipRect.Min.y && bg_y2 < table->BgClipRect.Max.y)\n            window->DrawList->AddLine(ImVec2(table->BorderX1, bg_y2), ImVec2(table->BorderX2, bg_y2), table->BorderColorStrong, border_size);\n    }\n\n    // End frozen rows (when we are past the last frozen row line, teleport cursor and alter clipping rectangle)\n    // We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and\n    // get the new cursor position.\n    if (unfreeze_rows_request)\n        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n            table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;\n    if (unfreeze_rows_actual)\n    {\n        IM_ASSERT(table->IsUnfrozenRows == false);\n        const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);\n        table->IsUnfrozenRows = true;\n        table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;\n\n        // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect\n        table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);\n        table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;\n        table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;\n        IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);\n\n        float row_height = table->RowPosY2 - table->RowPosY1;\n        table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;\n        table->RowPosY1 = table->RowPosY2 - row_height;\n        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        {\n            ImGuiTableColumn* column = &table->Columns[column_n];\n            column->DrawChannelCurrent = column->DrawChannelUnfrozen;\n            column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;\n        }\n\n        // Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y\n        SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);\n        table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);\n    }\n\n    if (!(table->RowFlags & ImGuiTableRowFlags_Headers))\n        table->RowBgColorCounter++;\n    table->IsInsideRow = false;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Columns changes\n//-------------------------------------------------------------------------\n// - TableGetColumnIndex()\n// - TableSetColumnIndex()\n// - TableNextColumn()\n// - TableBeginCell() [Internal]\n// - TableEndCell() [Internal]\n//-------------------------------------------------------------------------\n\nint ImGui::TableGetColumnIndex()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return 0;\n    return table->CurrentColumn;\n}\n\n// [Public] Append into a specific column\nbool ImGui::TableSetColumnIndex(int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return false;\n\n    if (table->CurrentColumn != column_n)\n    {\n        if (table->CurrentColumn != -1)\n            TableEndCell(table);\n        IM_ASSERT(column_n >= 0 && table->ColumnsCount);\n        TableBeginCell(table, column_n);\n    }\n\n    // Return whether the column is visible. User may choose to skip submitting items based on this return value,\n    // however they shouldn't skip submitting for columns that may have the tallest contribution to row height.\n    return table->Columns[column_n].IsRequestOutput;\n}\n\n// [Public] Append into the next column, wrap and create a new row when already on last column\nbool ImGui::TableNextColumn()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (!table)\n        return false;\n\n    if (table->IsInsideRow && table->CurrentColumn + 1 < table->ColumnsCount)\n    {\n        if (table->CurrentColumn != -1)\n            TableEndCell(table);\n        TableBeginCell(table, table->CurrentColumn + 1);\n    }\n    else\n    {\n        TableNextRow();\n        TableBeginCell(table, 0);\n    }\n\n    // Return whether the column is visible. User may choose to skip submitting items based on this return value,\n    // however they shouldn't skip submitting for columns that may have the tallest contribution to row height.\n    return table->Columns[table->CurrentColumn].IsRequestOutput;\n}\n\n\n// [Internal] Called by TableSetColumnIndex()/TableNextColumn()\n// This is called very frequently, so we need to be mindful of unnecessary overhead.\n// FIXME-TABLE FIXME-OPT: Could probably shortcut some things for non-active or clipped columns.\nvoid ImGui::TableBeginCell(ImGuiTable* table, int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTableColumn* column = &table->Columns[column_n];\n    ImGuiWindow* window = table->InnerWindow;\n    table->CurrentColumn = column_n;\n\n    // Start position is roughly ~~ CellRect.Min + CellPadding + Indent\n    float start_x = column->WorkMinX;\n    if (column->Flags & ImGuiTableColumnFlags_IndentEnable)\n        start_x += table->RowIndentOffsetX; // ~~ += window.DC.Indent.x - table->HostIndentX, except we locked it for the row.\n\n    window->DC.CursorPos.x = start_x;\n    window->DC.CursorPos.y = table->RowPosY1 + table->RowCellPaddingY;\n    window->DC.CursorMaxPos.x = window->DC.CursorPos.x;\n    window->DC.ColumnsOffset.x = start_x - window->Pos.x - window->DC.Indent.x; // FIXME-WORKRECT\n    window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x; // PrevLine.y is preserved. This allows users to call SameLine() to share LineSize between columns.\n    window->DC.CurrLineTextBaseOffset = table->RowTextBaseline;\n    window->DC.NavLayerCurrent = (ImGuiNavLayer)column->NavLayerCurrent;\n\n    // Note how WorkRect.Max.y is only set once during layout\n    window->WorkRect.Min.y = window->DC.CursorPos.y;\n    window->WorkRect.Min.x = column->WorkMinX;\n    window->WorkRect.Max.x = column->WorkMaxX;\n    window->DC.ItemWidth = column->ItemWidth;\n\n    window->SkipItems = column->IsSkipItems;\n    if (column->IsSkipItems)\n    {\n        g.LastItemData.ID = 0;\n        g.LastItemData.StatusFlags = 0;\n    }\n\n    if (table->Flags & ImGuiTableFlags_NoClip)\n    {\n        // FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.\n        table->DrawSplitter->SetCurrentChannel(window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);\n        //IM_ASSERT(table->DrawSplitter._Current == TABLE_DRAW_CHANNEL_NOCLIP);\n    }\n    else\n    {\n        // FIXME-TABLE: Could avoid this if draw channel is dummy channel?\n        SetWindowClipRectBeforeSetChannel(window, column->ClipRect);\n        table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);\n    }\n\n    // Logging\n    if (g.LogEnabled && !column->IsSkipItems)\n    {\n        LogRenderedText(&window->DC.CursorPos, \"|\");\n        g.LogLinePosY = FLT_MAX;\n    }\n}\n\n// [Internal] Called by TableNextRow()/TableSetColumnIndex()/TableNextColumn()\nvoid ImGui::TableEndCell(ImGuiTable* table)\n{\n    ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];\n    ImGuiWindow* window = table->InnerWindow;\n\n    if (window->DC.IsSetPos)\n        ErrorCheckUsingSetCursorPosToExtendParentBoundaries();\n\n    // Report maximum position so we can infer content size per column.\n    float* p_max_pos_x;\n    if (table->RowFlags & ImGuiTableRowFlags_Headers)\n        p_max_pos_x = &column->ContentMaxXHeadersUsed;  // Useful in case user submit contents in header row that is not a TableHeader() call\n    else\n        p_max_pos_x = table->IsUnfrozenRows ? &column->ContentMaxXUnfrozen : &column->ContentMaxXFrozen;\n    *p_max_pos_x = ImMax(*p_max_pos_x, window->DC.CursorMaxPos.x);\n    if (column->IsEnabled)\n        table->RowPosY2 = ImMax(table->RowPosY2, window->DC.CursorMaxPos.y + table->RowCellPaddingY);\n    column->ItemWidth = window->DC.ItemWidth;\n\n    // Propagate text baseline for the entire row\n    // FIXME-TABLE: Here we propagate text baseline from the last line of the cell.. instead of the first one.\n    table->RowTextBaseline = ImMax(table->RowTextBaseline, window->DC.PrevLineTextBaseOffset);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Columns width management\n//-------------------------------------------------------------------------\n// - TableGetMaxColumnWidth() [Internal]\n// - TableGetColumnWidthAuto() [Internal]\n// - TableSetColumnWidth()\n// - TableSetColumnWidthAutoSingle() [Internal]\n// - TableSetColumnWidthAutoAll() [Internal]\n// - TableUpdateColumnsWeightFromWidth() [Internal]\n//-------------------------------------------------------------------------\n\n// Maximum column content width given current layout. Use column->MinX so this value on a per-column basis.\nfloat ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n)\n{\n    const ImGuiTableColumn* column = &table->Columns[column_n];\n    float max_width = FLT_MAX;\n    const float min_column_distance = table->MinColumnWidth + table->CellPaddingX * 2.0f + table->CellSpacingX1 + table->CellSpacingX2;\n    if (table->Flags & ImGuiTableFlags_ScrollX)\n    {\n        // Frozen columns can't reach beyond visible width else scrolling will naturally break.\n        // (we use DisplayOrder as within a set of multiple frozen column reordering is possible)\n        if (column->DisplayOrder < table->FreezeColumnsRequest)\n        {\n            max_width = (table->InnerClipRect.Max.x - (table->FreezeColumnsRequest - column->DisplayOrder) * min_column_distance) - column->MinX;\n            max_width = max_width - table->OuterPaddingX - table->CellPaddingX - table->CellSpacingX2;\n        }\n    }\n    else if ((table->Flags & ImGuiTableFlags_NoKeepColumnsVisible) == 0)\n    {\n        // If horizontal scrolling if disabled, we apply a final lossless shrinking of columns in order to make\n        // sure they are all visible. Because of this we also know that all of the columns will always fit in\n        // table->WorkRect and therefore in table->InnerRect (because ScrollX is off)\n        // FIXME-TABLE: This is solved incorrectly but also quite a difficult problem to fix as we also want ClipRect width to match.\n        // See \"table_width_distrib\" and \"table_width_keep_visible\" tests\n        max_width = table->WorkRect.Max.x - (table->ColumnsEnabledCount - column->IndexWithinEnabledSet - 1) * min_column_distance - column->MinX;\n        //max_width -= table->CellSpacingX1;\n        max_width -= table->CellSpacingX2;\n        max_width -= table->CellPaddingX * 2.0f;\n        max_width -= table->OuterPaddingX;\n    }\n    return max_width;\n}\n\n// Note this is meant to be stored in column->WidthAuto, please generally use the WidthAuto field\nfloat ImGui::TableGetColumnWidthAuto(ImGuiTable* table, ImGuiTableColumn* column)\n{\n    const float content_width_body = ImMax(column->ContentMaxXFrozen, column->ContentMaxXUnfrozen) - column->WorkMinX;\n    const float content_width_headers = column->ContentMaxXHeadersIdeal - column->WorkMinX;\n    float width_auto = content_width_body;\n    if (!(column->Flags & ImGuiTableColumnFlags_NoHeaderWidth))\n        width_auto = ImMax(width_auto, content_width_headers);\n\n    // Non-resizable fixed columns preserve their requested width\n    if ((column->Flags & ImGuiTableColumnFlags_WidthFixed) && column->InitStretchWeightOrWidth > 0.0f)\n        if (!(table->Flags & ImGuiTableFlags_Resizable) || (column->Flags & ImGuiTableColumnFlags_NoResize))\n            width_auto = column->InitStretchWeightOrWidth;\n\n    return ImMax(width_auto, table->MinColumnWidth);\n}\n\n// 'width' = inner column width, without padding\nvoid ImGui::TableSetColumnWidth(int column_n, float width)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT(table != NULL && table->IsLayoutLocked == false);\n    IM_ASSERT(column_n >= 0 && column_n < table->ColumnsCount);\n    ImGuiTableColumn* column_0 = &table->Columns[column_n];\n    float column_0_width = width;\n\n    // Apply constraints early\n    // Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)\n    IM_ASSERT(table->MinColumnWidth > 0.0f);\n    const float min_width = table->MinColumnWidth;\n    const float max_width = ImMax(min_width, TableGetMaxColumnWidth(table, column_n));\n    column_0_width = ImClamp(column_0_width, min_width, max_width);\n    if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)\n        return;\n\n    //IMGUI_DEBUG_PRINT(\"TableSetColumnWidth(%d, %.1f->%.1f)\\n\", column_0_idx, column_0->WidthGiven, column_0_width);\n    ImGuiTableColumn* column_1 = (column_0->NextEnabledColumn != -1) ? &table->Columns[column_0->NextEnabledColumn] : NULL;\n\n    // In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns.\n    // - All fixed: easy.\n    // - All stretch: easy.\n    // - One or more fixed + one stretch: easy.\n    // - One or more fixed + more than one stretch: tricky.\n    // Qt when manual resize is enabled only supports a single _trailing_ stretch column, we support more cases here.\n\n    // When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1.\n    // FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user.\n    // Scenarios:\n    // - F1 F2 F3  resize from F1| or F2|   --> ok: alter ->WidthRequested of Fixed column. Subsequent columns will be offset.\n    // - F1 F2 F3  resize from F3|          --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered.\n    // - F1 F2 W3  resize from F1| or F2|   --> ok: alter ->WidthRequested of Fixed column. If active, ScrollX extent can be altered, but it doesn't make much sense as the Stretch column will always be minimal size.\n    // - F1 F2 W3  resize from W3|          --> ok: no-op (disabled by Resize Rule 1)\n    // - W1 W2 W3  resize from W1| or W2|   --> ok\n    // - W1 W2 W3  resize from W3|          --> ok: no-op (disabled by Resize Rule 1)\n    // - W1 F2 F3  resize from F3|          --> ok: no-op (disabled by Resize Rule 1)\n    // - W1 F2     resize from F2|          --> ok: no-op (disabled by Resize Rule 1)\n    // - W1 W2 F3  resize from W1| or W2|   --> ok\n    // - W1 F2 W3  resize from W1| or F2|   --> ok\n    // - F1 W2 F3  resize from W2|          --> ok\n    // - F1 W3 F2  resize from W3|          --> ok\n    // - W1 F2 F3  resize from W1|          --> ok: equivalent to resizing |F2. F3 will not move.\n    // - W1 F2 F3  resize from F2|          --> ok\n    // All resizes from a Wx columns are locking other columns.\n\n    // Possible improvements:\n    // - W1 W2 W3  resize W1|               --> to not be stuck, both W2 and W3 would stretch down. Seems possible to fix. Would be most beneficial to simplify resize of all-weighted columns.\n    // - W3 F1 F2  resize W3|               --> to not be stuck past F1|, both F1 and F2 would need to stretch down, which would be lossy or ambiguous. Seems hard to fix.\n\n    // [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout().\n\n    // If we have all Fixed columns OR resizing a Fixed column that doesn't come after a Stretch one, we can do an offsetting resize.\n    // This is the preferred resize path\n    if (column_0->Flags & ImGuiTableColumnFlags_WidthFixed)\n        if (!column_1 || table->LeftMostStretchedColumn == -1 || table->Columns[table->LeftMostStretchedColumn].DisplayOrder >= column_0->DisplayOrder)\n        {\n            column_0->WidthRequest = column_0_width;\n            table->IsSettingsDirty = true;\n            return;\n        }\n\n    // We can also use previous column if there's no next one (this is used when doing an auto-fit on the right-most stretch column)\n    if (column_1 == NULL)\n        column_1 = (column_0->PrevEnabledColumn != -1) ? &table->Columns[column_0->PrevEnabledColumn] : NULL;\n    if (column_1 == NULL)\n        return;\n\n    // Resizing from right-side of a Stretch column before a Fixed column forward sizing to left-side of fixed column.\n    // (old_a + old_b == new_a + new_b) --> (new_a == old_a + old_b - new_b)\n    float column_1_width = ImMax(column_1->WidthRequest - (column_0_width - column_0->WidthRequest), min_width);\n    column_0_width = column_0->WidthRequest + column_1->WidthRequest - column_1_width;\n    IM_ASSERT(column_0_width > 0.0f && column_1_width > 0.0f);\n    column_0->WidthRequest = column_0_width;\n    column_1->WidthRequest = column_1_width;\n    if ((column_0->Flags | column_1->Flags) & ImGuiTableColumnFlags_WidthStretch)\n        TableUpdateColumnsWeightFromWidth(table);\n    table->IsSettingsDirty = true;\n}\n\n// Disable clipping then auto-fit, will take 2 frames\n// (we don't take a shortcut for unclipped columns to reduce inconsistencies when e.g. resizing multiple columns)\nvoid ImGui::TableSetColumnWidthAutoSingle(ImGuiTable* table, int column_n)\n{\n    // Single auto width uses auto-fit\n    ImGuiTableColumn* column = &table->Columns[column_n];\n    if (!column->IsEnabled)\n        return;\n    column->CannotSkipItemsQueue = (1 << 0);\n    table->AutoFitSingleColumn = (ImGuiTableColumnIdx)column_n;\n}\n\nvoid ImGui::TableSetColumnWidthAutoAll(ImGuiTable* table)\n{\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (!column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_WidthStretch)) // Cannot reset weight of hidden stretch column\n            continue;\n        column->CannotSkipItemsQueue = (1 << 0);\n        column->AutoFitQueue = (1 << 1);\n    }\n}\n\nvoid ImGui::TableUpdateColumnsWeightFromWidth(ImGuiTable* table)\n{\n    IM_ASSERT(table->LeftMostStretchedColumn != -1 && table->RightMostStretchedColumn != -1);\n\n    // Measure existing quantities\n    float visible_weight = 0.0f;\n    float visible_width = 0.0f;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))\n            continue;\n        IM_ASSERT(column->StretchWeight > 0.0f);\n        visible_weight += column->StretchWeight;\n        visible_width += column->WidthRequest;\n    }\n    IM_ASSERT(visible_weight > 0.0f && visible_width > 0.0f);\n\n    // Apply new weights\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (!column->IsEnabled || !(column->Flags & ImGuiTableColumnFlags_WidthStretch))\n            continue;\n        column->StretchWeight = (column->WidthRequest / visible_width) * visible_weight;\n        IM_ASSERT(column->StretchWeight > 0.0f);\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Drawing\n//-------------------------------------------------------------------------\n// - TablePushBackgroundChannel() [Internal]\n// - TablePopBackgroundChannel() [Internal]\n// - TableSetupDrawChannels() [Internal]\n// - TableMergeDrawChannels() [Internal]\n// - TableGetColumnBorderCol() [Internal]\n// - TableDrawBorders() [Internal]\n//-------------------------------------------------------------------------\n\n// Bg2 is used by Selectable (and possibly other widgets) to render to the background.\n// Unlike our Bg0/1 channel which we uses for RowBg/CellBg/Borders and where we guarantee all shapes to be CPU-clipped, the Bg2 channel being widgets-facing will rely on regular ClipRect.\nvoid ImGui::TablePushBackgroundChannel()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiTable* table = g.CurrentTable;\n\n    // Optimization: avoid SetCurrentChannel() + PushClipRect()\n    table->HostBackupInnerClipRect = window->ClipRect;\n    SetWindowClipRectBeforeSetChannel(window, table->Bg2ClipRectForDrawCmd);\n    table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Bg2DrawChannelCurrent);\n}\n\nvoid ImGui::TablePopBackgroundChannel()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiTable* table = g.CurrentTable;\n    ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];\n\n    // Optimization: avoid PopClipRect() + SetCurrentChannel()\n    SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);\n    table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);\n}\n\n// Allocate draw channels. Called by TableUpdateLayout()\n// - We allocate them following storage order instead of display order so reordering columns won't needlessly\n//   increase overall dormant memory cost.\n// - We isolate headers draw commands in their own channels instead of just altering clip rects.\n//   This is in order to facilitate merging of draw commands.\n// - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels.\n// - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other\n//   channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged.\n// - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for\n//   horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4).\n// Draw channel allocation (before merging):\n// - NoClip                       --> 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call)\n// - Clip                         --> 2+D+N channels\n// - FreezeRows                   --> 2+D+N*2 (unless scrolling value is zero)\n// - FreezeRows || FreezeColunns  --> 3+D+N*2 (unless scrolling value is zero)\n// Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0.\nvoid ImGui::TableSetupDrawChannels(ImGuiTable* table)\n{\n    const int freeze_row_multiplier = (table->FreezeRowsCount > 0) ? 2 : 1;\n    const int channels_for_row = (table->Flags & ImGuiTableFlags_NoClip) ? 1 : table->ColumnsEnabledCount;\n    const int channels_for_bg = 1 + 1 * freeze_row_multiplier;\n    const int channels_for_dummy = (table->ColumnsEnabledCount < table->ColumnsCount || (memcmp(table->VisibleMaskByIndex, table->EnabledMaskByIndex, ImBitArrayGetStorageSizeInBytes(table->ColumnsCount)) != 0)) ? +1 : 0;\n    const int channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy;\n    table->DrawSplitter->Split(table->InnerWindow->DrawList, channels_total);\n    table->DummyDrawChannel = (ImGuiTableDrawChannelIdx)((channels_for_dummy > 0) ? channels_total - 1 : -1);\n    table->Bg2DrawChannelCurrent = TABLE_DRAW_CHANNEL_BG2_FROZEN;\n    table->Bg2DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)((table->FreezeRowsCount > 0) ? 2 + channels_for_row : TABLE_DRAW_CHANNEL_BG2_FROZEN);\n\n    int draw_channel_current = 2;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (column->IsVisibleX && column->IsVisibleY)\n        {\n            column->DrawChannelFrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current);\n            column->DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current + (table->FreezeRowsCount > 0 ? channels_for_row + 1 : 0));\n            if (!(table->Flags & ImGuiTableFlags_NoClip))\n                draw_channel_current++;\n        }\n        else\n        {\n            column->DrawChannelFrozen = column->DrawChannelUnfrozen = table->DummyDrawChannel;\n        }\n        column->DrawChannelCurrent = column->DrawChannelFrozen;\n    }\n\n    // Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default.\n    // All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect.\n    // (This technically isn't part of setting up draw channels, but is reasonably related to be done here)\n    table->BgClipRect = table->InnerClipRect;\n    table->Bg0ClipRectForDrawCmd = table->OuterWindow->ClipRect;\n    table->Bg2ClipRectForDrawCmd = table->HostClipRect;\n    IM_ASSERT(table->BgClipRect.Min.y <= table->BgClipRect.Max.y);\n}\n\n// This function reorder draw channels based on matching clip rectangle, to facilitate merging them. Called by EndTable().\n// For simplicity we call it TableMergeDrawChannels() but in fact it only reorder channels + overwrite ClipRect,\n// actual merging is done by table->DrawSplitter.Merge() which is called right after TableMergeDrawChannels().\n//\n// Columns where the contents didn't stray off their local clip rectangle can be merged. To achieve\n// this we merge their clip rect and make them contiguous in the channel list, so they can be merged\n// by the call to DrawSplitter.Merge() following to the call to this function.\n// We reorder draw commands by arranging them into a maximum of 4 distinct groups:\n//\n//   1 group:               2 groups:              2 groups:              4 groups:\n//   [ 0. ] no freeze       [ 0. ] row freeze      [ 01 ] col freeze      [ 01 ] row+col freeze\n//   [ .. ]  or no scroll   [ 2. ]  and v-scroll   [ .. ]  and h-scroll   [ 23 ]  and v+h-scroll\n//\n// Each column itself can use 1 channel (row freeze disabled) or 2 channels (row freeze enabled).\n// When the contents of a column didn't stray off its limit, we move its channels into the corresponding group\n// based on its position (within frozen rows/columns groups or not).\n// At the end of the operation our 1-4 groups will each have a ImDrawCmd using the same ClipRect.\n// This function assume that each column are pointing to a distinct draw channel,\n// otherwise merge_group->ChannelsCount will not match set bit count of merge_group->ChannelsMask.\n//\n// Column channels will not be merged into one of the 1-4 groups in the following cases:\n// - The contents stray off its clipping rectangle (we only compare the MaxX value, not the MinX value).\n//   Direct ImDrawList calls won't be taken into account by default, if you use them make sure the ImGui:: bounds\n//   matches, by e.g. calling SetCursorScreenPos().\n// - The channel uses more than one draw command itself. We drop all our attempt at merging stuff here..\n//   we could do better but it's going to be rare and probably not worth the hassle.\n// Columns for which the draw channel(s) haven't been merged with other will use their own ImDrawCmd.\n//\n// This function is particularly tricky to understand.. take a breath.\nvoid ImGui::TableMergeDrawChannels(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    ImDrawListSplitter* splitter = table->DrawSplitter;\n    const bool has_freeze_v = (table->FreezeRowsCount > 0);\n    const bool has_freeze_h = (table->FreezeColumnsCount > 0);\n    IM_ASSERT(splitter->_Current == 0);\n\n    // Track which groups we are going to attempt to merge, and which channels goes into each group.\n    struct MergeGroup\n    {\n        ImRect          ClipRect;\n        int             ChannelsCount = 0;\n        ImBitArrayPtr   ChannelsMask = NULL;\n    };\n    int merge_group_mask = 0x00;\n    MergeGroup merge_groups[4];\n\n    // Use a reusable temp buffer for the merge masks as they are dynamically sized.\n    const int max_draw_channels = (4 + table->ColumnsCount * 2);\n    const int size_for_masks_bitarrays_one = (int)ImBitArrayGetStorageSizeInBytes(max_draw_channels);\n    g.TempBuffer.reserve(size_for_masks_bitarrays_one * 5);\n    memset(g.TempBuffer.Data, 0, size_for_masks_bitarrays_one * 5);\n    for (int n = 0; n < IM_ARRAYSIZE(merge_groups); n++)\n        merge_groups[n].ChannelsMask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * n));\n    ImBitArrayPtr remaining_mask = (ImBitArrayPtr)(void*)(g.TempBuffer.Data + (size_for_masks_bitarrays_one * 4));\n\n    // 1. Scan channels and take note of those which can be merged\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        if (!IM_BITARRAY_TESTBIT(table->VisibleMaskByIndex, column_n))\n            continue;\n        ImGuiTableColumn* column = &table->Columns[column_n];\n\n        const int merge_group_sub_count = has_freeze_v ? 2 : 1;\n        for (int merge_group_sub_n = 0; merge_group_sub_n < merge_group_sub_count; merge_group_sub_n++)\n        {\n            const int channel_no = (merge_group_sub_n == 0) ? column->DrawChannelFrozen : column->DrawChannelUnfrozen;\n\n            // Don't attempt to merge if there are multiple draw calls within the column\n            ImDrawChannel* src_channel = &splitter->_Channels[channel_no];\n            if (src_channel->_CmdBuffer.Size > 0 && src_channel->_CmdBuffer.back().ElemCount == 0 && src_channel->_CmdBuffer.back().UserCallback == NULL) // Equivalent of PopUnusedDrawCmd()\n                src_channel->_CmdBuffer.pop_back();\n            if (src_channel->_CmdBuffer.Size != 1)\n                continue;\n\n            // Find out the width of this merge group and check if it will fit in our column\n            // (note that we assume that rendering didn't stray on the left direction. we should need a CursorMinPos to detect it)\n            if (!(column->Flags & ImGuiTableColumnFlags_NoClip))\n            {\n                float content_max_x;\n                if (!has_freeze_v)\n                    content_max_x = ImMax(column->ContentMaxXUnfrozen, column->ContentMaxXHeadersUsed); // No row freeze\n                else if (merge_group_sub_n == 0)\n                    content_max_x = ImMax(column->ContentMaxXFrozen, column->ContentMaxXHeadersUsed);   // Row freeze: use width before freeze\n                else\n                    content_max_x = column->ContentMaxXUnfrozen;                                        // Row freeze: use width after freeze\n                if (content_max_x > column->ClipRect.Max.x)\n                    continue;\n            }\n\n            const int merge_group_n = (has_freeze_h && column_n < table->FreezeColumnsCount ? 0 : 1) + (has_freeze_v && merge_group_sub_n == 0 ? 0 : 2);\n            IM_ASSERT(channel_no < max_draw_channels);\n            MergeGroup* merge_group = &merge_groups[merge_group_n];\n            if (merge_group->ChannelsCount == 0)\n                merge_group->ClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);\n            ImBitArraySetBit(merge_group->ChannelsMask, channel_no);\n            merge_group->ChannelsCount++;\n            merge_group->ClipRect.Add(src_channel->_CmdBuffer[0].ClipRect);\n            merge_group_mask |= (1 << merge_group_n);\n        }\n\n        // Invalidate current draw channel\n        // (we don't clear DrawChannelFrozen/DrawChannelUnfrozen solely to facilitate debugging/later inspection of data)\n        column->DrawChannelCurrent = (ImGuiTableDrawChannelIdx)-1;\n    }\n\n    // [DEBUG] Display merge groups\n#if 0\n    if (g.IO.KeyShift)\n        for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++)\n        {\n            MergeGroup* merge_group = &merge_groups[merge_group_n];\n            if (merge_group->ChannelsCount == 0)\n                continue;\n            char buf[32];\n            ImFormatString(buf, 32, \"MG%d:%d\", merge_group_n, merge_group->ChannelsCount);\n            ImVec2 text_pos = merge_group->ClipRect.Min + ImVec2(4, 4);\n            ImVec2 text_size = CalcTextSize(buf, NULL);\n            GetForegroundDrawList()->AddRectFilled(text_pos, text_pos + text_size, IM_COL32(0, 0, 0, 255));\n            GetForegroundDrawList()->AddText(text_pos, IM_COL32(255, 255, 0, 255), buf, NULL);\n            GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 255, 0, 255));\n        }\n#endif\n\n    // 2. Rewrite channel list in our preferred order\n    if (merge_group_mask != 0)\n    {\n        // We skip channel 0 (Bg0/Bg1) and 1 (Bg2 frozen) from the shuffling since they won't move - see channels allocation in TableSetupDrawChannels().\n        const int LEADING_DRAW_CHANNELS = 2;\n        g.DrawChannelsTempMergeBuffer.resize(splitter->_Count - LEADING_DRAW_CHANNELS); // Use shared temporary storage so the allocation gets amortized\n        ImDrawChannel* dst_tmp = g.DrawChannelsTempMergeBuffer.Data;\n        ImBitArraySetBitRange(remaining_mask, LEADING_DRAW_CHANNELS, splitter->_Count);\n        ImBitArrayClearBit(remaining_mask, table->Bg2DrawChannelUnfrozen);\n        IM_ASSERT(has_freeze_v == false || table->Bg2DrawChannelUnfrozen != TABLE_DRAW_CHANNEL_BG2_FROZEN);\n        int remaining_count = splitter->_Count - (has_freeze_v ? LEADING_DRAW_CHANNELS + 1 : LEADING_DRAW_CHANNELS);\n        //ImRect host_rect = (table->InnerWindow == table->OuterWindow) ? table->InnerClipRect : table->HostClipRect;\n        ImRect host_rect = table->HostClipRect;\n        for (int merge_group_n = 0; merge_group_n < IM_ARRAYSIZE(merge_groups); merge_group_n++)\n        {\n            if (int merge_channels_count = merge_groups[merge_group_n].ChannelsCount)\n            {\n                MergeGroup* merge_group = &merge_groups[merge_group_n];\n                ImRect merge_clip_rect = merge_group->ClipRect;\n\n                // Extend outer-most clip limits to match those of host, so draw calls can be merged even if\n                // outer-most columns have some outer padding offsetting them from their parent ClipRect.\n                // The principal cases this is dealing with are:\n                // - On a same-window table (not scrolling = single group), all fitting columns ClipRect -> will extend and match host ClipRect -> will merge\n                // - Columns can use padding and have left-most ClipRect.Min.x and right-most ClipRect.Max.x != from host ClipRect -> will extend and match host ClipRect -> will merge\n                // FIXME-TABLE FIXME-WORKRECT: We are wasting a merge opportunity on tables without scrolling if column doesn't fit\n                // within host clip rect, solely because of the half-padding difference between window->WorkRect and window->InnerClipRect.\n                if ((merge_group_n & 1) == 0 || !has_freeze_h)\n                    merge_clip_rect.Min.x = ImMin(merge_clip_rect.Min.x, host_rect.Min.x);\n                if ((merge_group_n & 2) == 0 || !has_freeze_v)\n                    merge_clip_rect.Min.y = ImMin(merge_clip_rect.Min.y, host_rect.Min.y);\n                if ((merge_group_n & 1) != 0)\n                    merge_clip_rect.Max.x = ImMax(merge_clip_rect.Max.x, host_rect.Max.x);\n                if ((merge_group_n & 2) != 0 && (table->Flags & ImGuiTableFlags_NoHostExtendY) == 0)\n                    merge_clip_rect.Max.y = ImMax(merge_clip_rect.Max.y, host_rect.Max.y);\n                //GetForegroundDrawList()->AddRect(merge_group->ClipRect.Min, merge_group->ClipRect.Max, IM_COL32(255, 0, 0, 200), 0.0f, 0, 1.0f); // [DEBUG]\n                //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Min, merge_clip_rect.Min, IM_COL32(255, 100, 0, 200));\n                //GetForegroundDrawList()->AddLine(merge_group->ClipRect.Max, merge_clip_rect.Max, IM_COL32(255, 100, 0, 200));\n                remaining_count -= merge_group->ChannelsCount;\n                for (int n = 0; n < (size_for_masks_bitarrays_one >> 2); n++)\n                    remaining_mask[n] &= ~merge_group->ChannelsMask[n];\n                for (int n = 0; n < splitter->_Count && merge_channels_count != 0; n++)\n                {\n                    // Copy + overwrite new clip rect\n                    if (!IM_BITARRAY_TESTBIT(merge_group->ChannelsMask, n))\n                        continue;\n                    IM_BITARRAY_CLEARBIT(merge_group->ChannelsMask, n);\n                    merge_channels_count--;\n\n                    ImDrawChannel* channel = &splitter->_Channels[n];\n                    IM_ASSERT(channel->_CmdBuffer.Size == 1 && merge_clip_rect.Contains(ImRect(channel->_CmdBuffer[0].ClipRect)));\n                    channel->_CmdBuffer[0].ClipRect = merge_clip_rect.ToVec4();\n                    memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));\n                }\n            }\n\n            // Make sure Bg2DrawChannelUnfrozen appears in the middle of our groups (whereas Bg0/Bg1 and Bg2 frozen are fixed to 0 and 1)\n            if (merge_group_n == 1 && has_freeze_v)\n                memcpy(dst_tmp++, &splitter->_Channels[table->Bg2DrawChannelUnfrozen], sizeof(ImDrawChannel));\n        }\n\n        // Append unmergeable channels that we didn't reorder at the end of the list\n        for (int n = 0; n < splitter->_Count && remaining_count != 0; n++)\n        {\n            if (!IM_BITARRAY_TESTBIT(remaining_mask, n))\n                continue;\n            ImDrawChannel* channel = &splitter->_Channels[n];\n            memcpy(dst_tmp++, channel, sizeof(ImDrawChannel));\n            remaining_count--;\n        }\n        IM_ASSERT(dst_tmp == g.DrawChannelsTempMergeBuffer.Data + g.DrawChannelsTempMergeBuffer.Size);\n        memcpy(splitter->_Channels.Data + LEADING_DRAW_CHANNELS, g.DrawChannelsTempMergeBuffer.Data, (splitter->_Count - LEADING_DRAW_CHANNELS) * sizeof(ImDrawChannel));\n    }\n}\n\nstatic ImU32 TableGetColumnBorderCol(ImGuiTable* table, int order_n, int column_n)\n{\n    const bool is_hovered = (table->HoveredColumnBorder == column_n);\n    const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent);\n    const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1);\n    if (is_resized || is_hovered)\n        return ImGui::GetColorU32(is_resized ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered);\n    if (is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)))\n        return table->BorderColorStrong;\n    return table->BorderColorLight;\n}\n\n// FIXME-TABLE: This is a mess, need to redesign how we render borders (as some are also done in TableEndRow)\nvoid ImGui::TableDrawBorders(ImGuiTable* table)\n{\n    ImGuiWindow* inner_window = table->InnerWindow;\n    if (!table->OuterWindow->ClipRect.Overlaps(table->OuterRect))\n        return;\n\n    ImDrawList* inner_drawlist = inner_window->DrawList;\n    table->DrawSplitter->SetCurrentChannel(inner_drawlist, TABLE_DRAW_CHANNEL_BG0);\n    inner_drawlist->PushClipRect(table->Bg0ClipRectForDrawCmd.Min, table->Bg0ClipRectForDrawCmd.Max, false);\n\n    // Draw inner border and resizing feedback\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    const float border_size = TABLE_BORDER_SIZE;\n    const float draw_y1 = ImMax(table->InnerRect.Min.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight) + ((table->Flags & ImGuiTableFlags_BordersOuterH) ? 1.0f : 0.0f);\n    const float draw_y2_body = table->InnerRect.Max.y;\n    const float draw_y2_head = table->IsUsingHeaders ? ImMin(table->InnerRect.Max.y, (table->FreezeRowsCount >= 1 ? table->InnerRect.Min.y : table->WorkRect.Min.y) + table_instance->LastTopHeadersRowHeight) : draw_y1;\n    if (table->Flags & ImGuiTableFlags_BordersInnerV)\n    {\n        for (int order_n = 0; order_n < table->ColumnsCount; order_n++)\n        {\n            if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))\n                continue;\n\n            const int column_n = table->DisplayOrderToIndex[order_n];\n            ImGuiTableColumn* column = &table->Columns[column_n];\n            const bool is_hovered = (table->HoveredColumnBorder == column_n);\n            const bool is_resized = (table->ResizedColumn == column_n) && (table->InstanceInteracted == table->InstanceCurrent);\n            const bool is_resizable = (column->Flags & (ImGuiTableColumnFlags_NoResize | ImGuiTableColumnFlags_NoDirectResize_)) == 0;\n            const bool is_frozen_separator = (table->FreezeColumnsCount == order_n + 1);\n            if (column->MaxX > table->InnerClipRect.Max.x && !is_resized)\n                continue;\n\n            // Decide whether right-most column is visible\n            if (column->NextEnabledColumn == -1 && !is_resizable)\n                if ((table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame || (table->Flags & ImGuiTableFlags_NoHostExtendX))\n                    continue;\n            if (column->MaxX <= column->ClipRect.Min.x) // FIXME-TABLE FIXME-STYLE: Assume BorderSize==1, this is problematic if we want to increase the border size..\n                continue;\n\n            // Draw in outer window so right-most column won't be clipped\n            // Always draw full height border when being resized/hovered, or on the delimitation of frozen column scrolling.\n            float draw_y2 = (is_hovered || is_resized || is_frozen_separator || (table->Flags & (ImGuiTableFlags_NoBordersInBody | ImGuiTableFlags_NoBordersInBodyUntilResize)) == 0) ? draw_y2_body : draw_y2_head;\n            if (draw_y2 > draw_y1)\n                inner_drawlist->AddLine(ImVec2(column->MaxX, draw_y1), ImVec2(column->MaxX, draw_y2), TableGetColumnBorderCol(table, order_n, column_n), border_size);\n        }\n    }\n\n    // Draw outer border\n    // FIXME: could use AddRect or explicit VLine/HLine helper?\n    if (table->Flags & ImGuiTableFlags_BordersOuter)\n    {\n        // Display outer border offset by 1 which is a simple way to display it without adding an extra draw call\n        // (Without the offset, in outer_window it would be rendered behind cells, because child windows are above their\n        // parent. In inner_window, it won't reach out over scrollbars. Another weird solution would be to display part\n        // of it in inner window, and the part that's over scrollbars in the outer window..)\n        // Either solution currently won't allow us to use a larger border size: the border would clipped.\n        const ImRect outer_border = table->OuterRect;\n        const ImU32 outer_col = table->BorderColorStrong;\n        if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)\n        {\n            inner_drawlist->AddRect(outer_border.Min, outer_border.Max + ImVec2(1, 1), outer_col, 0.0f, 0, border_size);\n        }\n        else if (table->Flags & ImGuiTableFlags_BordersOuterV)\n        {\n            inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Min.x, outer_border.Max.y), outer_col, border_size);\n            inner_drawlist->AddLine(ImVec2(outer_border.Max.x, outer_border.Min.y), outer_border.Max, outer_col, border_size);\n        }\n        else if (table->Flags & ImGuiTableFlags_BordersOuterH)\n        {\n            inner_drawlist->AddLine(outer_border.Min, ImVec2(outer_border.Max.x, outer_border.Min.y), outer_col, border_size);\n            inner_drawlist->AddLine(ImVec2(outer_border.Min.x, outer_border.Max.y), outer_border.Max, outer_col, border_size);\n        }\n    }\n    if ((table->Flags & ImGuiTableFlags_BordersInnerH) && table->RowPosY2 < table->OuterRect.Max.y)\n    {\n        // Draw bottom-most row border between it is above outer border.\n        const float border_y = table->RowPosY2;\n        if (border_y >= table->BgClipRect.Min.y && border_y < table->BgClipRect.Max.y)\n            inner_drawlist->AddLine(ImVec2(table->BorderX1, border_y), ImVec2(table->BorderX2, border_y), table->BorderColorLight, border_size);\n    }\n\n    inner_drawlist->PopClipRect();\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Sorting\n//-------------------------------------------------------------------------\n// - TableGetSortSpecs()\n// - TableFixColumnSortDirection() [Internal]\n// - TableGetColumnNextSortDirection() [Internal]\n// - TableSetColumnSortDirection() [Internal]\n// - TableSortSpecsSanitize() [Internal]\n// - TableSortSpecsBuild() [Internal]\n//-------------------------------------------------------------------------\n\n// Return NULL if no sort specs (most often when ImGuiTableFlags_Sortable is not set)\n// When 'sort_specs->SpecsDirty == true' you should sort your data. It will be true when sorting specs have\n// changed since last call, or the first time. Make sure to set 'SpecsDirty = false' after sorting,\n// else you may wastefully sort your data every frame!\n// Lifetime: don't hold on this pointer over multiple frames or past any subsequent call to BeginTable()!\nImGuiTableSortSpecs* ImGui::TableGetSortSpecs()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT(table != NULL);\n\n    if (!(table->Flags & ImGuiTableFlags_Sortable))\n        return NULL;\n\n    // Require layout (in case TableHeadersRow() hasn't been called) as it may alter IsSortSpecsDirty in some paths.\n    if (!table->IsLayoutLocked)\n        TableUpdateLayout(table);\n\n    TableSortSpecsBuild(table);\n    return &table->SortSpecs;\n}\n\nstatic inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n)\n{\n    IM_ASSERT(n < column->SortDirectionsAvailCount);\n    return (column->SortDirectionsAvailList >> (n << 1)) & 0x03;\n}\n\n// Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending)\nvoid ImGui::TableFixColumnSortDirection(ImGuiTable* table, ImGuiTableColumn* column)\n{\n    if (column->SortOrder == -1 || (column->SortDirectionsAvailMask & (1 << column->SortDirection)) != 0)\n        return;\n    column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0);\n    table->IsSortSpecsDirty = true;\n}\n\n// Calculate next sort direction that would be set after clicking the column\n// - If the PreferSortDescending flag is set, we will default to a Descending direction on the first click.\n// - Note that the PreferSortAscending flag is never checked, it is essentially the default and therefore a no-op.\nIM_STATIC_ASSERT(ImGuiSortDirection_None == 0 && ImGuiSortDirection_Ascending == 1 && ImGuiSortDirection_Descending == 2);\nImGuiSortDirection ImGui::TableGetColumnNextSortDirection(ImGuiTableColumn* column)\n{\n    IM_ASSERT(column->SortDirectionsAvailCount > 0);\n    if (column->SortOrder == -1)\n        return TableGetColumnAvailSortDirection(column, 0);\n    for (int n = 0; n < 3; n++)\n        if (column->SortDirection == TableGetColumnAvailSortDirection(column, n))\n            return TableGetColumnAvailSortDirection(column, (n + 1) % column->SortDirectionsAvailCount);\n    IM_ASSERT(0);\n    return ImGuiSortDirection_None;\n}\n\n// Note that the NoSortAscending/NoSortDescending flags are processed in TableSortSpecsSanitize(), and they may change/revert\n// the value of SortDirection. We could technically also do it here but it would be unnecessary and duplicate code.\nvoid ImGui::TableSetColumnSortDirection(int column_n, ImGuiSortDirection sort_direction, bool append_to_sort_specs)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n\n    if (!(table->Flags & ImGuiTableFlags_SortMulti))\n        append_to_sort_specs = false;\n    if (!(table->Flags & ImGuiTableFlags_SortTristate))\n        IM_ASSERT(sort_direction != ImGuiSortDirection_None);\n\n    ImGuiTableColumnIdx sort_order_max = 0;\n    if (append_to_sort_specs)\n        for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)\n            sort_order_max = ImMax(sort_order_max, table->Columns[other_column_n].SortOrder);\n\n    ImGuiTableColumn* column = &table->Columns[column_n];\n    column->SortDirection = (ImU8)sort_direction;\n    if (column->SortDirection == ImGuiSortDirection_None)\n        column->SortOrder = -1;\n    else if (column->SortOrder == -1 || !append_to_sort_specs)\n        column->SortOrder = append_to_sort_specs ? sort_order_max + 1 : 0;\n\n    for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)\n    {\n        ImGuiTableColumn* other_column = &table->Columns[other_column_n];\n        if (other_column != column && !append_to_sort_specs)\n            other_column->SortOrder = -1;\n        TableFixColumnSortDirection(table, other_column);\n    }\n    table->IsSettingsDirty = true;\n    table->IsSortSpecsDirty = true;\n}\n\nvoid ImGui::TableSortSpecsSanitize(ImGuiTable* table)\n{\n    IM_ASSERT(table->Flags & ImGuiTableFlags_Sortable);\n\n    // Clear SortOrder from hidden column and verify that there's no gap or duplicate.\n    int sort_order_count = 0;\n    ImU64 sort_order_mask = 0x00;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (column->SortOrder != -1 && !column->IsEnabled)\n            column->SortOrder = -1;\n        if (column->SortOrder == -1)\n            continue;\n        sort_order_count++;\n        sort_order_mask |= ((ImU64)1 << column->SortOrder);\n        IM_ASSERT(sort_order_count < (int)sizeof(sort_order_mask) * 8);\n    }\n\n    const bool need_fix_linearize = ((ImU64)1 << sort_order_count) != (sort_order_mask + 1);\n    const bool need_fix_single_sort_order = (sort_order_count > 1) && !(table->Flags & ImGuiTableFlags_SortMulti);\n    if (need_fix_linearize || need_fix_single_sort_order)\n    {\n        ImU64 fixed_mask = 0x00;\n        for (int sort_n = 0; sort_n < sort_order_count; sort_n++)\n        {\n            // Fix: Rewrite sort order fields if needed so they have no gap or duplicate.\n            // (e.g. SortOrder 0 disappeared, SortOrder 1..2 exists --> rewrite then as SortOrder 0..1)\n            int column_with_smallest_sort_order = -1;\n            for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n                if ((fixed_mask & ((ImU64)1 << (ImU64)column_n)) == 0 && table->Columns[column_n].SortOrder != -1)\n                    if (column_with_smallest_sort_order == -1 || table->Columns[column_n].SortOrder < table->Columns[column_with_smallest_sort_order].SortOrder)\n                        column_with_smallest_sort_order = column_n;\n            IM_ASSERT(column_with_smallest_sort_order != -1);\n            fixed_mask |= ((ImU64)1 << column_with_smallest_sort_order);\n            table->Columns[column_with_smallest_sort_order].SortOrder = (ImGuiTableColumnIdx)sort_n;\n\n            // Fix: Make sure only one column has a SortOrder if ImGuiTableFlags_MultiSortable is not set.\n            if (need_fix_single_sort_order)\n            {\n                sort_order_count = 1;\n                for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n                    if (column_n != column_with_smallest_sort_order)\n                        table->Columns[column_n].SortOrder = -1;\n                break;\n            }\n        }\n    }\n\n    // Fallback default sort order (if no column with the ImGuiTableColumnFlags_DefaultSort flag)\n    if (sort_order_count == 0 && !(table->Flags & ImGuiTableFlags_SortTristate))\n        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        {\n            ImGuiTableColumn* column = &table->Columns[column_n];\n            if (column->IsEnabled && !(column->Flags & ImGuiTableColumnFlags_NoSort))\n            {\n                sort_order_count = 1;\n                column->SortOrder = 0;\n                column->SortDirection = (ImU8)TableGetColumnAvailSortDirection(column, 0);\n                break;\n            }\n        }\n\n    table->SortSpecsCount = (ImGuiTableColumnIdx)sort_order_count;\n}\n\nvoid ImGui::TableSortSpecsBuild(ImGuiTable* table)\n{\n    bool dirty = table->IsSortSpecsDirty;\n    if (dirty)\n    {\n        TableSortSpecsSanitize(table);\n        table->SortSpecsMulti.resize(table->SortSpecsCount <= 1 ? 0 : table->SortSpecsCount);\n        table->SortSpecs.SpecsDirty = true; // Mark as dirty for user\n        table->IsSortSpecsDirty = false; // Mark as not dirty for us\n    }\n\n    // Write output\n    ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data;\n    if (dirty && sort_specs != NULL)\n        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        {\n            ImGuiTableColumn* column = &table->Columns[column_n];\n            if (column->SortOrder == -1)\n                continue;\n            IM_ASSERT(column->SortOrder < table->SortSpecsCount);\n            ImGuiTableColumnSortSpecs* sort_spec = &sort_specs[column->SortOrder];\n            sort_spec->ColumnUserID = column->UserID;\n            sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n;\n            sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;\n            sort_spec->SortDirection = column->SortDirection;\n        }\n\n    table->SortSpecs.Specs = sort_specs;\n    table->SortSpecs.SpecsCount = table->SortSpecsCount;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Headers\n//-------------------------------------------------------------------------\n// - TableGetHeaderRowHeight() [Internal]\n// - TableHeadersRow()\n// - TableHeader()\n// - TableAngledHeadersRow()\n// - TableAngledHeadersRowEx() [Internal]\n//-------------------------------------------------------------------------\n\nfloat ImGui::TableGetHeaderRowHeight()\n{\n    // Caring for a minor edge case:\n    // Calculate row height, for the unlikely case that some labels may be taller than others.\n    // If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height.\n    // In your custom header row you may omit this all together and just call TableNextRow() without a height...\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    float row_height = g.FontSize;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))\n            if ((table->Columns[column_n].Flags & ImGuiTableColumnFlags_NoHeaderLabel) == 0)\n                row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(table, column_n)).y);\n    return row_height + g.Style.CellPadding.y * 2.0f;\n}\n\nfloat ImGui::TableGetHeaderAngledMaxLabelWidth()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    float width = 0.0f;\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        if (IM_BITARRAY_TESTBIT(table->EnabledMaskByIndex, column_n))\n            if (table->Columns[column_n].Flags & ImGuiTableColumnFlags_AngledHeader)\n                width = ImMax(width, CalcTextSize(TableGetColumnName(table, column_n), NULL, true).x);\n    return width + g.Style.CellPadding.x * 2.0f;\n}\n\n// [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn().\n// The intent is that advanced users willing to create customized headers would not need to use this helper\n// and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets.\n// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.\n// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy.\n// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.\nvoid ImGui::TableHeadersRow()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT(table != NULL && \"Need to call TableHeadersRow() after BeginTable()!\");\n\n    // Layout if not already done (this is automatically done by TableNextRow, we do it here solely to facilitate stepping in debugger as it is frequent to step in TableUpdateLayout)\n    if (!table->IsLayoutLocked)\n        TableUpdateLayout(table);\n\n    // Open row\n    const float row_height = TableGetHeaderRowHeight();\n    TableNextRow(ImGuiTableRowFlags_Headers, row_height);\n    const float row_y1 = GetCursorScreenPos().y;\n    if (table->HostSkipItems) // Merely an optimization, you may skip in your own code.\n        return;\n\n    const int columns_count = TableGetColumnCount();\n    for (int column_n = 0; column_n < columns_count; column_n++)\n    {\n        if (!TableSetColumnIndex(column_n))\n            continue;\n\n        // Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)\n        // In your own code you may omit the PushID/PopID all-together, provided you know they won't collide.\n        const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? \"\" : TableGetColumnName(column_n);\n        PushID(column_n);\n        TableHeader(name);\n        PopID();\n    }\n\n    // Allow opening popup from the right-most section after the last column.\n    ImVec2 mouse_pos = ImGui::GetMousePos();\n    if (IsMouseReleased(1) && TableGetHoveredColumn() == columns_count)\n        if (mouse_pos.y >= row_y1 && mouse_pos.y < row_y1 + row_height)\n            TableOpenContextMenu(columns_count); // Will open a non-column-specific popup.\n}\n\n// Emit a column header (text + optional sort order)\n// We cpu-clip text here so that all columns headers can be merged into a same draw call.\n// Note that because of how we cpu-clip and display sorting indicators, you _cannot_ use SameLine() after a TableHeader()\nvoid ImGui::TableHeader(const char* label)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    ImGuiTable* table = g.CurrentTable;\n    IM_ASSERT(table != NULL && \"Need to call TableHeader() after BeginTable()!\");\n    IM_ASSERT(table->CurrentColumn != -1);\n    const int column_n = table->CurrentColumn;\n    ImGuiTableColumn* column = &table->Columns[column_n];\n\n    // Label\n    if (label == NULL)\n        label = \"\";\n    const char* label_end = FindRenderedTextEnd(label);\n    ImVec2 label_size = CalcTextSize(label, label_end, true);\n    ImVec2 label_pos = window->DC.CursorPos;\n\n    // If we already got a row height, there's use that.\n    // FIXME-TABLE: Padding problem if the correct outer-padding CellBgRect strays off our ClipRect?\n    ImRect cell_r = TableGetCellBgRect(table, column_n);\n    float label_height = ImMax(label_size.y, table->RowMinHeight - table->RowCellPaddingY * 2.0f);\n\n    // Calculate ideal size for sort order arrow\n    float w_arrow = 0.0f;\n    float w_sort_text = 0.0f;\n    bool sort_arrow = false;\n    char sort_order_suf[4] = \"\";\n    const float ARROW_SCALE = 0.65f;\n    if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))\n    {\n        w_arrow = ImTrunc(g.FontSize * ARROW_SCALE + g.Style.FramePadding.x);\n        if (column->SortOrder != -1)\n            sort_arrow = true;\n        if (column->SortOrder > 0)\n        {\n            ImFormatString(sort_order_suf, IM_ARRAYSIZE(sort_order_suf), \"%d\", column->SortOrder + 1);\n            w_sort_text = g.Style.ItemInnerSpacing.x + CalcTextSize(sort_order_suf).x;\n        }\n    }\n\n    // We feed our unclipped width to the column without writing on CursorMaxPos, so that column is still considered for merging.\n    float max_pos_x = label_pos.x + label_size.x + w_sort_text + w_arrow;\n    column->ContentMaxXHeadersUsed = ImMax(column->ContentMaxXHeadersUsed, sort_arrow ? cell_r.Max.x : ImMin(max_pos_x, cell_r.Max.x));\n    column->ContentMaxXHeadersIdeal = ImMax(column->ContentMaxXHeadersIdeal, max_pos_x);\n\n    // Keep header highlighted when context menu is open.\n    ImGuiID id = window->GetID(label);\n    ImRect bb(cell_r.Min.x, cell_r.Min.y, cell_r.Max.x, ImMax(cell_r.Max.y, cell_r.Min.y + label_height + g.Style.CellPadding.y * 2.0f));\n    ItemSize(ImVec2(0.0f, label_height)); // Don't declare unclipped width, it'll be fed ContentMaxPosHeadersIdeal\n    if (!ItemAdd(bb, id))\n        return;\n\n    //GetForegroundDrawList()->AddRect(cell_r.Min, cell_r.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]\n    //GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 0, 0, 255)); // [DEBUG]\n\n    // Using AllowOverlap mode because we cover the whole cell, and we want user to be able to submit subsequent items.\n    const bool highlight = (table->HighlightColumnHeader == column_n);\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_AllowOverlap);\n    if (held || hovered || highlight)\n    {\n        const ImU32 col = GetColorU32(held ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);\n        //RenderFrame(bb.Min, bb.Max, col, false, 0.0f);\n        TableSetBgColor(ImGuiTableBgTarget_CellBg, col, table->CurrentColumn);\n    }\n    else\n    {\n        // Submit single cell bg color in the case we didn't submit a full header row\n        if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)\n            TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);\n    }\n    RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);\n    if (held)\n        table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;\n    window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;\n\n    // Drag and drop to re-order columns.\n    // FIXME-TABLE: Scroll request while reordering a column and it lands out of the scrolling zone.\n    if (held && (table->Flags & ImGuiTableFlags_Reorderable) && IsMouseDragging(0) && !g.DragDropActive)\n    {\n        // While moving a column it will jump on the other side of the mouse, so we also test for MouseDelta.x\n        table->ReorderColumn = (ImGuiTableColumnIdx)column_n;\n        table->InstanceInteracted = table->InstanceCurrent;\n\n        // We don't reorder: through the frozen<>unfrozen line, or through a column that is marked with ImGuiTableColumnFlags_NoReorder.\n        if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < cell_r.Min.x)\n            if (ImGuiTableColumn* prev_column = (column->PrevEnabledColumn != -1) ? &table->Columns[column->PrevEnabledColumn] : NULL)\n                if (!((column->Flags | prev_column->Flags) & ImGuiTableColumnFlags_NoReorder))\n                    if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (prev_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))\n                        table->ReorderColumnDir = -1;\n        if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > cell_r.Max.x)\n            if (ImGuiTableColumn* next_column = (column->NextEnabledColumn != -1) ? &table->Columns[column->NextEnabledColumn] : NULL)\n                if (!((column->Flags | next_column->Flags) & ImGuiTableColumnFlags_NoReorder))\n                    if ((column->IndexWithinEnabledSet < table->FreezeColumnsRequest) == (next_column->IndexWithinEnabledSet < table->FreezeColumnsRequest))\n                        table->ReorderColumnDir = +1;\n    }\n\n    // Sort order arrow\n    const float ellipsis_max = ImMax(cell_r.Max.x - w_arrow - w_sort_text, label_pos.x);\n    if ((table->Flags & ImGuiTableFlags_Sortable) && !(column->Flags & ImGuiTableColumnFlags_NoSort))\n    {\n        if (column->SortOrder != -1)\n        {\n            float x = ImMax(cell_r.Min.x, cell_r.Max.x - w_arrow - w_sort_text);\n            float y = label_pos.y;\n            if (column->SortOrder > 0)\n            {\n                PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_Text, 0.70f));\n                RenderText(ImVec2(x + g.Style.ItemInnerSpacing.x, y), sort_order_suf);\n                PopStyleColor();\n                x += w_sort_text;\n            }\n            RenderArrow(window->DrawList, ImVec2(x, y), GetColorU32(ImGuiCol_Text), column->SortDirection == ImGuiSortDirection_Ascending ? ImGuiDir_Up : ImGuiDir_Down, ARROW_SCALE);\n        }\n\n        // Handle clicking on column header to adjust Sort Order\n        if (pressed && table->ReorderColumn != column_n)\n        {\n            ImGuiSortDirection sort_direction = TableGetColumnNextSortDirection(column);\n            TableSetColumnSortDirection(column_n, sort_direction, g.IO.KeyShift);\n        }\n    }\n\n    // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will\n    // be merged into a single draw call.\n    //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);\n    RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);\n\n    const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);\n    if (text_clipped && hovered && g.ActiveId == 0)\n        SetItemTooltip(\"%.*s\", (int)(label_end - label), label);\n\n    // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden\n    if (IsMouseReleased(1) && IsItemHovered())\n        TableOpenContextMenu(column_n);\n}\n\n// Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets.\n// FIXME: highlight without ImGuiTableFlags_HighlightHoveredColumn\n// FIXME: No hit-testing/button on the angled header.\nvoid ImGui::TableAngledHeadersRow()\n{\n    ImGuiContext& g = *GImGui;\n    TableAngledHeadersRowEx(g.Style.TableAngledHeadersAngle, 0.0f);\n}\n\nvoid ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImDrawList* draw_list = window->DrawList;\n    IM_ASSERT(table != NULL && \"Need to call TableHeadersRow() after BeginTable()!\");\n    IM_ASSERT(table->CurrentRow == -1 && \"Must be first row\");\n\n    if (max_label_width == 0.0f)\n        max_label_width = TableGetHeaderAngledMaxLabelWidth();\n\n    // Angle argument expressed in (-IM_PI/2 .. +IM_PI/2) as it is easier to think about for user.\n    const bool flip_label = (angle < 0.0f);\n    angle -= IM_PI * 0.5f;\n    const float cos_a = ImCos(angle);\n    const float sin_a = ImSin(angle);\n    const float label_cos_a = flip_label ? ImCos(angle + IM_PI) : cos_a;\n    const float label_sin_a = flip_label ? ImSin(angle + IM_PI) : sin_a;\n    const ImVec2 unit_right = ImVec2(cos_a, sin_a);\n\n    // Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow()\n    // FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other.\n    const float header_height = table->RowCellPaddingY * 2.0f + g.FontSize;\n    const float row_height = ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y);\n    const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a);\n    table->AngledHeadersHeight = row_height;\n    table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f;\n\n    // Declare row, override and draw our own background\n    TableNextRow(ImGuiTableRowFlags_Headers, row_height);\n    TableNextColumn();\n    table->DrawSplitter->SetCurrentChannel(draw_list, TABLE_DRAW_CHANNEL_BG0);\n    float clip_rect_min_x = table->BgClipRect.Min.x;\n    if (table->FreezeColumnsCount > 0)\n        clip_rect_min_x = ImMax(clip_rect_min_x, table->Columns[table->FreezeColumnsCount - 1].MaxX);\n    TableSetBgColor(ImGuiTableBgTarget_RowBg0, 0); // Cancel\n    PushClipRect(table->BgClipRect.Min, table->BgClipRect.Max, false); // Span all columns\n    draw_list->AddRectFilled(table->BgClipRect.Min, table->BgClipRect.Max, GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color.\n    PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns\n\n    const ImRect row_r(table->WorkRect.Min.x, table->BgClipRect.Min.y, table->WorkRect.Max.x, window->DC.CursorPos.y + row_height);\n    const ImGuiID row_id = GetID(\"##AngledHeaders\");\n    ButtonBehavior(row_r, row_id, NULL, NULL);\n    KeepAliveID(row_id);\n\n    ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);\n    int highlight_column_n = table->HighlightColumnHeader;\n    if (highlight_column_n == -1 && table->HoveredColumnBody != -1)\n        if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive)))\n            highlight_column_n = table->HoveredColumnBody;\n\n    float max_x = 0.0f;\n    for (int pass = 0; pass < 2; pass++)\n        for (int order_n = 0; order_n < table->ColumnsCount; order_n++)\n        {\n            if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))\n                continue;\n            const int column_n = table->DisplayOrderToIndex[order_n];\n            ImGuiTableColumn* column = &table->Columns[column_n];\n            if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here.\n                continue;\n\n            ImVec2 bg_shape[4];\n            bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y);\n            bg_shape[1] = ImVec2(column->MinX, row_r.Max.y);\n            bg_shape[2] = bg_shape[1] + header_angled_vector;\n            bg_shape[3] = bg_shape[0] + header_angled_vector;\n            if (pass == 0)\n            {\n                // Draw shape\n                draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_TableHeaderBg));\n                if (column_n == highlight_column_n)\n                    draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_Header)); // Highlight on hover\n                //draw_list->AddQuad(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_TableBorderLight), 1.0f);\n                max_x = ImMax(max_x, bg_shape[3].x);\n\n                // Draw label (first draw at an offset where RenderTextXXX() function won't meddle with applying current ClipRect, then transform to final offset)\n                // FIXME: May be worth tidying up all those operations to make them easier to understand.\n                const char* label_name = TableGetColumnName(table, column_n);\n                const float clip_width = max_label_width - (sin_a * table->RowCellPaddingY);\n                ImRect label_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width + (flip_label ? 0.0f : table->CellPaddingX), header_height + table->RowCellPaddingY));\n                ImVec2 label_size = CalcTextSize(label_name, NULL, true);\n                ImVec2 label_off = ImVec2(flip_label ? ImMax(0.0f, max_label_width - label_size.x - table->CellPaddingX) : table->CellPaddingX, table->RowCellPaddingY);\n                int vtx_idx_begin = draw_list->_VtxCurrentIdx;\n                RenderTextEllipsis(draw_list, label_r.Min + label_off, label_r.Max, label_r.Max.x, label_r.Max.x, label_name, NULL, &label_size);\n                //if (g.IO.KeyShift) { draw_list->AddRect(label_r.Min, label_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 2.0f); }\n                int vtx_idx_end = draw_list->_VtxCurrentIdx;\n\n                // Rotate and offset label\n                ImVec2 pivot_in = label_r.GetBL();\n                ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y) + (flip_label ? (unit_right * clip_width) : ImVec2(header_height, 0.0f));\n                ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset\n            }\n            if (pass == 1)\n            {\n                // Draw border\n                draw_list->AddLine(bg_shape[0], bg_shape[3], TableGetColumnBorderCol(table, order_n, column_n));\n            }\n        }\n    PopClipRect();\n    PopClipRect();\n    table->TempData->AngledheadersExtraWidth = ImMax(0.0f, max_x - table->Columns[table->RightMostEnabledColumn].MaxX);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Context Menu\n//-------------------------------------------------------------------------\n// - TableOpenContextMenu() [Internal]\n// - TableBeginContextMenuPopup() [Internal]\n// - TableDrawDefaultContextMenu() [Internal]\n//-------------------------------------------------------------------------\n\n// Use -1 to open menu not specific to a given column.\nvoid ImGui::TableOpenContextMenu(int column_n)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTable* table = g.CurrentTable;\n    if (column_n == -1 && table->CurrentColumn != -1)   // When called within a column automatically use this one (for consistency)\n        column_n = table->CurrentColumn;\n    if (column_n == table->ColumnsCount)                // To facilitate using with TableGetHoveredColumn()\n        column_n = -1;\n    IM_ASSERT(column_n >= -1 && column_n < table->ColumnsCount);\n    if (table->Flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable))\n    {\n        table->IsContextPopupOpen = true;\n        table->ContextPopupColumn = (ImGuiTableColumnIdx)column_n;\n        table->InstanceInteracted = table->InstanceCurrent;\n        const ImGuiID context_menu_id = ImHashStr(\"##ContextMenu\", 0, table->ID);\n        OpenPopupEx(context_menu_id, ImGuiPopupFlags_None);\n    }\n}\n\nbool ImGui::TableBeginContextMenuPopup(ImGuiTable* table)\n{\n    if (!table->IsContextPopupOpen || table->InstanceCurrent != table->InstanceInteracted)\n        return false;\n    const ImGuiID context_menu_id = ImHashStr(\"##ContextMenu\", 0, table->ID);\n    if (BeginPopupEx(context_menu_id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings))\n        return true;\n    table->IsContextPopupOpen = false;\n    return false;\n}\n\n// Output context menu into current window (generally a popup)\n// FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data?\n// Sections to display are pulled from 'flags_for_section_to_display', which is typically == table->Flags.\n// - ImGuiTableFlags_Resizable   -> display Sizing menu items\n// - ImGuiTableFlags_Reorderable -> display \"Reset Order\"\n////- ImGuiTableFlags_Sortable   -> display sorting options (disabled)\n// - ImGuiTableFlags_Hideable    -> display columns visibility menu items\n// It means if you have a custom context menus you can call this section and omit some sections, and add your own.\nvoid ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags_for_section_to_display)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    bool want_separator = false;\n    const int column_n = (table->ContextPopupColumn >= 0 && table->ContextPopupColumn < table->ColumnsCount) ? table->ContextPopupColumn : -1;\n    ImGuiTableColumn* column = (column_n != -1) ? &table->Columns[column_n] : NULL;\n\n    // Sizing\n    if (flags_for_section_to_display & ImGuiTableFlags_Resizable)\n    {\n        if (column != NULL)\n        {\n            const bool can_resize = !(column->Flags & ImGuiTableColumnFlags_NoResize) && column->IsEnabled;\n            if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableSizeOne), NULL, false, can_resize)) // \"###SizeOne\"\n                TableSetColumnWidthAutoSingle(table, column_n);\n        }\n\n        const char* size_all_desc;\n        if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount && (table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame)\n            size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllFit);        // \"###SizeAll\" All fixed\n        else\n            size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllDefault);    // \"###SizeAll\" All stretch or mixed\n        if (MenuItem(size_all_desc, NULL))\n            TableSetColumnWidthAutoAll(table);\n        want_separator = true;\n    }\n\n    // Ordering\n    if (flags_for_section_to_display & ImGuiTableFlags_Reorderable)\n    {\n        if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder))\n            table->IsResetDisplayOrderRequest = true;\n        want_separator = true;\n    }\n\n    // Reset all (should work but seems unnecessary/noisy to expose?)\n    //if (MenuItem(\"Reset all\"))\n    //    table->IsResetAllRequest = true;\n\n    // Sorting\n    // (modify TableOpenContextMenu() to add _Sortable flag if enabling this)\n#if 0\n    if ((flags_for_section_to_display & ImGuiTableFlags_Sortable) && column != NULL && (column->Flags & ImGuiTableColumnFlags_NoSort) == 0)\n    {\n        if (want_separator)\n            Separator();\n        want_separator = true;\n\n        bool append_to_sort_specs = g.IO.KeyShift;\n        if (MenuItem(\"Sort in Ascending Order\", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Ascending, (column->Flags & ImGuiTableColumnFlags_NoSortAscending) == 0))\n            TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Ascending, append_to_sort_specs);\n        if (MenuItem(\"Sort in Descending Order\", NULL, column->SortOrder != -1 && column->SortDirection == ImGuiSortDirection_Descending, (column->Flags & ImGuiTableColumnFlags_NoSortDescending) == 0))\n            TableSetColumnSortDirection(table, column_n, ImGuiSortDirection_Descending, append_to_sort_specs);\n    }\n#endif\n\n    // Hiding / Visibility\n    if (flags_for_section_to_display & ImGuiTableFlags_Hideable)\n    {\n        if (want_separator)\n            Separator();\n        want_separator = true;\n\n        PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);\n        for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)\n        {\n            ImGuiTableColumn* other_column = &table->Columns[other_column_n];\n            if (other_column->Flags & ImGuiTableColumnFlags_Disabled)\n                continue;\n\n            const char* name = TableGetColumnName(table, other_column_n);\n            if (name == NULL || name[0] == 0)\n                name = \"<Unknown>\";\n\n            // Make sure we can't hide the last active column\n            bool menu_item_active = (other_column->Flags & ImGuiTableColumnFlags_NoHide) ? false : true;\n            if (other_column->IsUserEnabled && table->ColumnsEnabledCount <= 1)\n                menu_item_active = false;\n            if (MenuItem(name, NULL, other_column->IsUserEnabled, menu_item_active))\n                other_column->IsUserEnabledNextFrame = !other_column->IsUserEnabled;\n        }\n        PopItemFlag();\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Settings (.ini data)\n//-------------------------------------------------------------------------\n// FIXME: The binding/finding/creating flow are too confusing.\n//-------------------------------------------------------------------------\n// - TableSettingsInit() [Internal]\n// - TableSettingsCalcChunkSize() [Internal]\n// - TableSettingsCreate() [Internal]\n// - TableSettingsFindByID() [Internal]\n// - TableGetBoundSettings() [Internal]\n// - TableResetSettings()\n// - TableSaveSettings() [Internal]\n// - TableLoadSettings() [Internal]\n// - TableSettingsHandler_ClearAll() [Internal]\n// - TableSettingsHandler_ApplyAll() [Internal]\n// - TableSettingsHandler_ReadOpen() [Internal]\n// - TableSettingsHandler_ReadLine() [Internal]\n// - TableSettingsHandler_WriteAll() [Internal]\n// - TableSettingsInstallHandler() [Internal]\n//-------------------------------------------------------------------------\n// [Init] 1: TableSettingsHandler_ReadXXXX()   Load and parse .ini file into TableSettings.\n// [Main] 2: TableLoadSettings()               When table is created, bind Table to TableSettings, serialize TableSettings data into Table.\n// [Main] 3: TableSaveSettings()               When table properties are modified, serialize Table data into bound or new TableSettings, mark .ini as dirty.\n// [Main] 4: TableSettingsHandler_WriteAll()   When .ini file is dirty (which can come from other source), save TableSettings into .ini file.\n//-------------------------------------------------------------------------\n\n// Clear and initialize empty settings instance\nstatic void TableSettingsInit(ImGuiTableSettings* settings, ImGuiID id, int columns_count, int columns_count_max)\n{\n    IM_PLACEMENT_NEW(settings) ImGuiTableSettings();\n    ImGuiTableColumnSettings* settings_column = settings->GetColumnSettings();\n    for (int n = 0; n < columns_count_max; n++, settings_column++)\n        IM_PLACEMENT_NEW(settings_column) ImGuiTableColumnSettings();\n    settings->ID = id;\n    settings->ColumnsCount = (ImGuiTableColumnIdx)columns_count;\n    settings->ColumnsCountMax = (ImGuiTableColumnIdx)columns_count_max;\n    settings->WantApply = true;\n}\n\nstatic size_t TableSettingsCalcChunkSize(int columns_count)\n{\n    return sizeof(ImGuiTableSettings) + (size_t)columns_count * sizeof(ImGuiTableColumnSettings);\n}\n\nImGuiTableSettings* ImGui::TableSettingsCreate(ImGuiID id, int columns_count)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTableSettings* settings = g.SettingsTables.alloc_chunk(TableSettingsCalcChunkSize(columns_count));\n    TableSettingsInit(settings, id, columns_count, columns_count);\n    return settings;\n}\n\n// Find existing settings\nImGuiTableSettings* ImGui::TableSettingsFindByID(ImGuiID id)\n{\n    // FIXME-OPT: Might want to store a lookup map for this?\n    ImGuiContext& g = *GImGui;\n    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))\n        if (settings->ID == id)\n            return settings;\n    return NULL;\n}\n\n// Get settings for a given table, NULL if none\nImGuiTableSettings* ImGui::TableGetBoundSettings(ImGuiTable* table)\n{\n    if (table->SettingsOffset != -1)\n    {\n        ImGuiContext& g = *GImGui;\n        ImGuiTableSettings* settings = g.SettingsTables.ptr_from_offset(table->SettingsOffset);\n        IM_ASSERT(settings->ID == table->ID);\n        if (settings->ColumnsCountMax >= table->ColumnsCount)\n            return settings; // OK\n        settings->ID = 0; // Invalidate storage, we won't fit because of a count change\n    }\n    return NULL;\n}\n\n// Restore initial state of table (with or without saved settings)\nvoid ImGui::TableResetSettings(ImGuiTable* table)\n{\n    table->IsInitializing = table->IsSettingsDirty = true;\n    table->IsResetAllRequest = false;\n    table->IsSettingsRequestLoad = false;                   // Don't reload from ini\n    table->SettingsLoadedFlags = ImGuiTableFlags_None;      // Mark as nothing loaded so our initialized data becomes authoritative\n}\n\nvoid ImGui::TableSaveSettings(ImGuiTable* table)\n{\n    table->IsSettingsDirty = false;\n    if (table->Flags & ImGuiTableFlags_NoSavedSettings)\n        return;\n\n    // Bind or create settings data\n    ImGuiContext& g = *GImGui;\n    ImGuiTableSettings* settings = TableGetBoundSettings(table);\n    if (settings == NULL)\n    {\n        settings = TableSettingsCreate(table->ID, table->ColumnsCount);\n        table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings);\n    }\n    settings->ColumnsCount = (ImGuiTableColumnIdx)table->ColumnsCount;\n\n    // Serialize ImGuiTable/ImGuiTableColumn into ImGuiTableSettings/ImGuiTableColumnSettings\n    IM_ASSERT(settings->ID == table->ID);\n    IM_ASSERT(settings->ColumnsCount == table->ColumnsCount && settings->ColumnsCountMax >= settings->ColumnsCount);\n    ImGuiTableColumn* column = table->Columns.Data;\n    ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();\n\n    bool save_ref_scale = false;\n    settings->SaveFlags = ImGuiTableFlags_None;\n    for (int n = 0; n < table->ColumnsCount; n++, column++, column_settings++)\n    {\n        const float width_or_weight = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? column->StretchWeight : column->WidthRequest;\n        column_settings->WidthOrWeight = width_or_weight;\n        column_settings->Index = (ImGuiTableColumnIdx)n;\n        column_settings->DisplayOrder = column->DisplayOrder;\n        column_settings->SortOrder = column->SortOrder;\n        column_settings->SortDirection = column->SortDirection;\n        column_settings->IsEnabled = column->IsUserEnabled;\n        column_settings->IsStretch = (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? 1 : 0;\n        if ((column->Flags & ImGuiTableColumnFlags_WidthStretch) == 0)\n            save_ref_scale = true;\n\n        // We skip saving some data in the .ini file when they are unnecessary to restore our state.\n        // Note that fixed width where initial width was derived from auto-fit will always be saved as InitStretchWeightOrWidth will be 0.0f.\n        // FIXME-TABLE: We don't have logic to easily compare SortOrder to DefaultSortOrder yet so it's always saved when present.\n        if (width_or_weight != column->InitStretchWeightOrWidth)\n            settings->SaveFlags |= ImGuiTableFlags_Resizable;\n        if (column->DisplayOrder != n)\n            settings->SaveFlags |= ImGuiTableFlags_Reorderable;\n        if (column->SortOrder != -1)\n            settings->SaveFlags |= ImGuiTableFlags_Sortable;\n        if (column->IsUserEnabled != ((column->Flags & ImGuiTableColumnFlags_DefaultHide) == 0))\n            settings->SaveFlags |= ImGuiTableFlags_Hideable;\n    }\n    settings->SaveFlags &= table->Flags;\n    settings->RefScale = save_ref_scale ? table->RefScale : 0.0f;\n\n    MarkIniSettingsDirty();\n}\n\nvoid ImGui::TableLoadSettings(ImGuiTable* table)\n{\n    ImGuiContext& g = *GImGui;\n    table->IsSettingsRequestLoad = false;\n    if (table->Flags & ImGuiTableFlags_NoSavedSettings)\n        return;\n\n    // Bind settings\n    ImGuiTableSettings* settings;\n    if (table->SettingsOffset == -1)\n    {\n        settings = TableSettingsFindByID(table->ID);\n        if (settings == NULL)\n            return;\n        if (settings->ColumnsCount != table->ColumnsCount) // Allow settings if columns count changed. We could otherwise decide to return...\n            table->IsSettingsDirty = true;\n        table->SettingsOffset = g.SettingsTables.offset_from_ptr(settings);\n    }\n    else\n    {\n        settings = TableGetBoundSettings(table);\n    }\n\n    table->SettingsLoadedFlags = settings->SaveFlags;\n    table->RefScale = settings->RefScale;\n\n    // Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn\n    ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();\n    ImU64 display_order_mask = 0;\n    for (int data_n = 0; data_n < settings->ColumnsCount; data_n++, column_settings++)\n    {\n        int column_n = column_settings->Index;\n        if (column_n < 0 || column_n >= table->ColumnsCount)\n            continue;\n\n        ImGuiTableColumn* column = &table->Columns[column_n];\n        if (settings->SaveFlags & ImGuiTableFlags_Resizable)\n        {\n            if (column_settings->IsStretch)\n                column->StretchWeight = column_settings->WidthOrWeight;\n            else\n                column->WidthRequest = column_settings->WidthOrWeight;\n            column->AutoFitQueue = 0x00;\n        }\n        if (settings->SaveFlags & ImGuiTableFlags_Reorderable)\n            column->DisplayOrder = column_settings->DisplayOrder;\n        else\n            column->DisplayOrder = (ImGuiTableColumnIdx)column_n;\n        display_order_mask |= (ImU64)1 << column->DisplayOrder;\n        column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled;\n        column->SortOrder = column_settings->SortOrder;\n        column->SortDirection = column_settings->SortDirection;\n    }\n\n    // Validate and fix invalid display order data\n    const ImU64 expected_display_order_mask = (settings->ColumnsCount == 64) ? ~0 : ((ImU64)1 << settings->ColumnsCount) - 1;\n    if (display_order_mask != expected_display_order_mask)\n        for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n            table->Columns[column_n].DisplayOrder = (ImGuiTableColumnIdx)column_n;\n\n    // Rebuild index\n    for (int column_n = 0; column_n < table->ColumnsCount; column_n++)\n        table->DisplayOrderToIndex[table->Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)column_n;\n}\n\nstatic void TableSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*)\n{\n    ImGuiContext& g = *ctx;\n    for (int i = 0; i != g.Tables.GetMapSize(); i++)\n        if (ImGuiTable* table = g.Tables.TryGetMapData(i))\n            table->SettingsOffset = -1;\n    g.SettingsTables.clear();\n}\n\n// Apply to existing windows (if any)\nstatic void TableSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*)\n{\n    ImGuiContext& g = *ctx;\n    for (int i = 0; i != g.Tables.GetMapSize(); i++)\n        if (ImGuiTable* table = g.Tables.TryGetMapData(i))\n        {\n            table->IsSettingsRequestLoad = true;\n            table->SettingsOffset = -1;\n        }\n}\n\nstatic void* TableSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)\n{\n    ImGuiID id = 0;\n    int columns_count = 0;\n    if (sscanf(name, \"0x%08X,%d\", &id, &columns_count) < 2)\n        return NULL;\n\n    if (ImGuiTableSettings* settings = ImGui::TableSettingsFindByID(id))\n    {\n        if (settings->ColumnsCountMax >= columns_count)\n        {\n            TableSettingsInit(settings, id, columns_count, settings->ColumnsCountMax); // Recycle\n            return settings;\n        }\n        settings->ID = 0; // Invalidate storage, we won't fit because of a count change\n    }\n    return ImGui::TableSettingsCreate(id, columns_count);\n}\n\nstatic void TableSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line)\n{\n    // \"Column 0  UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v\"\n    ImGuiTableSettings* settings = (ImGuiTableSettings*)entry;\n    float f = 0.0f;\n    int column_n = 0, r = 0, n = 0;\n\n    if (sscanf(line, \"RefScale=%f\", &f) == 1) { settings->RefScale = f; return; }\n\n    if (sscanf(line, \"Column %d%n\", &column_n, &r) == 1)\n    {\n        if (column_n < 0 || column_n >= settings->ColumnsCount)\n            return;\n        line = ImStrSkipBlank(line + r);\n        char c = 0;\n        ImGuiTableColumnSettings* column = settings->GetColumnSettings() + column_n;\n        column->Index = (ImGuiTableColumnIdx)column_n;\n        if (sscanf(line, \"UserID=0x%08X%n\", (ImU32*)&n, &r)==1) { line = ImStrSkipBlank(line + r); column->UserID = (ImGuiID)n; }\n        if (sscanf(line, \"Width=%d%n\", &n, &r) == 1)            { line = ImStrSkipBlank(line + r); column->WidthOrWeight = (float)n; column->IsStretch = 0; settings->SaveFlags |= ImGuiTableFlags_Resizable; }\n        if (sscanf(line, \"Weight=%f%n\", &f, &r) == 1)           { line = ImStrSkipBlank(line + r); column->WidthOrWeight = f; column->IsStretch = 1; settings->SaveFlags |= ImGuiTableFlags_Resizable; }\n        if (sscanf(line, \"Visible=%d%n\", &n, &r) == 1)          { line = ImStrSkipBlank(line + r); column->IsEnabled = (ImU8)n; settings->SaveFlags |= ImGuiTableFlags_Hideable; }\n        if (sscanf(line, \"Order=%d%n\", &n, &r) == 1)            { line = ImStrSkipBlank(line + r); column->DisplayOrder = (ImGuiTableColumnIdx)n; settings->SaveFlags |= ImGuiTableFlags_Reorderable; }\n        if (sscanf(line, \"Sort=%d%c%n\", &n, &c, &r) == 2)       { line = ImStrSkipBlank(line + r); column->SortOrder = (ImGuiTableColumnIdx)n; column->SortDirection = (c == '^') ? ImGuiSortDirection_Descending : ImGuiSortDirection_Ascending; settings->SaveFlags |= ImGuiTableFlags_Sortable; }\n    }\n}\n\nstatic void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)\n{\n    ImGuiContext& g = *ctx;\n    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))\n    {\n        if (settings->ID == 0) // Skip ditched settings\n            continue;\n\n        // TableSaveSettings() may clear some of those flags when we establish that the data can be stripped\n        // (e.g. Order was unchanged)\n        const bool save_size    = (settings->SaveFlags & ImGuiTableFlags_Resizable) != 0;\n        const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0;\n        const bool save_order   = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0;\n        const bool save_sort    = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0;\n        if (!save_size && !save_visible && !save_order && !save_sort)\n            continue;\n\n        buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve\n        buf->appendf(\"[%s][0x%08X,%d]\\n\", handler->TypeName, settings->ID, settings->ColumnsCount);\n        if (settings->RefScale != 0.0f)\n            buf->appendf(\"RefScale=%g\\n\", settings->RefScale);\n        ImGuiTableColumnSettings* column = settings->GetColumnSettings();\n        for (int column_n = 0; column_n < settings->ColumnsCount; column_n++, column++)\n        {\n            // \"Column 0  UserID=0x42AD2D21 Width=100 Visible=1 Order=0 Sort=0v\"\n            bool save_column = column->UserID != 0 || save_size || save_visible || save_order || (save_sort && column->SortOrder != -1);\n            if (!save_column)\n                continue;\n            buf->appendf(\"Column %-2d\", column_n);\n            if (column->UserID != 0)                    { buf->appendf(\" UserID=%08X\", column->UserID); }\n            if (save_size && column->IsStretch)         { buf->appendf(\" Weight=%.4f\", column->WidthOrWeight); }\n            if (save_size && !column->IsStretch)        { buf->appendf(\" Width=%d\", (int)column->WidthOrWeight); }\n            if (save_visible)                           { buf->appendf(\" Visible=%d\", column->IsEnabled); }\n            if (save_order)                             { buf->appendf(\" Order=%d\", column->DisplayOrder); }\n            if (save_sort && column->SortOrder != -1)   { buf->appendf(\" Sort=%d%c\", column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? 'v' : '^'); }\n            buf->append(\"\\n\");\n        }\n        buf->append(\"\\n\");\n    }\n}\n\nvoid ImGui::TableSettingsAddSettingsHandler()\n{\n    ImGuiSettingsHandler ini_handler;\n    ini_handler.TypeName = \"Table\";\n    ini_handler.TypeHash = ImHashStr(\"Table\");\n    ini_handler.ClearAllFn = TableSettingsHandler_ClearAll;\n    ini_handler.ReadOpenFn = TableSettingsHandler_ReadOpen;\n    ini_handler.ReadLineFn = TableSettingsHandler_ReadLine;\n    ini_handler.ApplyAllFn = TableSettingsHandler_ApplyAll;\n    ini_handler.WriteAllFn = TableSettingsHandler_WriteAll;\n    AddSettingsHandler(&ini_handler);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Garbage Collection\n//-------------------------------------------------------------------------\n// - TableRemove() [Internal]\n// - TableGcCompactTransientBuffers() [Internal]\n// - TableGcCompactSettings() [Internal]\n//-------------------------------------------------------------------------\n\n// Remove Table (currently only used by TestEngine)\nvoid ImGui::TableRemove(ImGuiTable* table)\n{\n    //IMGUI_DEBUG_PRINT(\"TableRemove() id=0x%08X\\n\", table->ID);\n    ImGuiContext& g = *GImGui;\n    int table_idx = g.Tables.GetIndex(table);\n    //memset(table->RawData.Data, 0, table->RawData.size_in_bytes());\n    //memset(table, 0, sizeof(ImGuiTable));\n    g.Tables.Remove(table->ID, table);\n    g.TablesLastTimeActive[table_idx] = -1.0f;\n}\n\n// Free up/compact internal Table buffers for when it gets unused\nvoid ImGui::TableGcCompactTransientBuffers(ImGuiTable* table)\n{\n    //IMGUI_DEBUG_PRINT(\"TableGcCompactTransientBuffers() id=0x%08X\\n\", table->ID);\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(table->MemoryCompacted == false);\n    table->SortSpecs.Specs = NULL;\n    table->SortSpecsMulti.clear();\n    table->IsSortSpecsDirty = true; // FIXME: In theory shouldn't have to leak into user performing a sort on resume.\n    table->ColumnsNames.clear();\n    table->MemoryCompacted = true;\n    for (int n = 0; n < table->ColumnsCount; n++)\n        table->Columns[n].NameOffset = -1;\n    g.TablesLastTimeActive[g.Tables.GetIndex(table)] = -1.0f;\n}\n\nvoid ImGui::TableGcCompactTransientBuffers(ImGuiTableTempData* temp_data)\n{\n    temp_data->DrawSplitter.ClearFreeMemory();\n    temp_data->LastTimeActive = -1.0f;\n}\n\n// Compact and remove unused settings data (currently only used by TestEngine)\nvoid ImGui::TableGcCompactSettings()\n{\n    ImGuiContext& g = *GImGui;\n    int required_memory = 0;\n    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))\n        if (settings->ID != 0)\n            required_memory += (int)TableSettingsCalcChunkSize(settings->ColumnsCount);\n    if (required_memory == g.SettingsTables.Buf.Size)\n        return;\n    ImChunkStream<ImGuiTableSettings> new_chunk_stream;\n    new_chunk_stream.Buf.reserve(required_memory);\n    for (ImGuiTableSettings* settings = g.SettingsTables.begin(); settings != NULL; settings = g.SettingsTables.next_chunk(settings))\n        if (settings->ID != 0)\n            memcpy(new_chunk_stream.alloc_chunk(TableSettingsCalcChunkSize(settings->ColumnsCount)), settings, TableSettingsCalcChunkSize(settings->ColumnsCount));\n    g.SettingsTables.swap(new_chunk_stream);\n}\n\n\n//-------------------------------------------------------------------------\n// [SECTION] Tables: Debugging\n//-------------------------------------------------------------------------\n// - DebugNodeTable() [Internal]\n//-------------------------------------------------------------------------\n\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n\nstatic const char* DebugNodeTableGetSizingPolicyDesc(ImGuiTableFlags sizing_policy)\n{\n    sizing_policy &= ImGuiTableFlags_SizingMask_;\n    if (sizing_policy == ImGuiTableFlags_SizingFixedFit)    { return \"FixedFit\"; }\n    if (sizing_policy == ImGuiTableFlags_SizingFixedSame)   { return \"FixedSame\"; }\n    if (sizing_policy == ImGuiTableFlags_SizingStretchProp) { return \"StretchProp\"; }\n    if (sizing_policy == ImGuiTableFlags_SizingStretchSame) { return \"StretchSame\"; }\n    return \"N/A\";\n}\n\nvoid ImGui::DebugNodeTable(ImGuiTable* table)\n{\n    const bool is_active = (table->LastFrameActive >= GetFrameCount() - 2); // Note that fully clipped early out scrolling tables will appear as inactive here.\n    if (!is_active) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); }\n    bool open = TreeNode(table, \"Table 0x%08X (%d columns, in '%s')%s\", table->ID, table->ColumnsCount, table->OuterWindow->Name, is_active ? \"\" : \" *Inactive*\");\n    if (!is_active) { PopStyleColor(); }\n    if (IsItemHovered())\n        GetForegroundDrawList()->AddRect(table->OuterRect.Min, table->OuterRect.Max, IM_COL32(255, 255, 0, 255));\n    if (IsItemVisible() && table->HoveredColumnBody != -1)\n        GetForegroundDrawList()->AddRect(GetItemRectMin(), GetItemRectMax(), IM_COL32(255, 255, 0, 255));\n    if (!open)\n        return;\n    if (table->InstanceCurrent > 0)\n        Text(\"** %d instances of same table! Some data below will refer to last instance.\", table->InstanceCurrent + 1);\n    bool clear_settings = SmallButton(\"Clear settings\");\n    BulletText(\"OuterRect: Pos: (%.1f,%.1f) Size: (%.1f,%.1f) Sizing: '%s'\", table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.GetWidth(), table->OuterRect.GetHeight(), DebugNodeTableGetSizingPolicyDesc(table->Flags));\n    BulletText(\"ColumnsGivenWidth: %.1f, ColumnsAutoFitWidth: %.1f, InnerWidth: %.1f%s\", table->ColumnsGivenWidth, table->ColumnsAutoFitWidth, table->InnerWidth, table->InnerWidth == 0.0f ? \" (auto)\" : \"\");\n    BulletText(\"CellPaddingX: %.1f, CellSpacingX: %.1f/%.1f, OuterPaddingX: %.1f\", table->CellPaddingX, table->CellSpacingX1, table->CellSpacingX2, table->OuterPaddingX);\n    BulletText(\"HoveredColumnBody: %d, HoveredColumnBorder: %d\", table->HoveredColumnBody, table->HoveredColumnBorder);\n    BulletText(\"ResizedColumn: %d, ReorderColumn: %d, HeldHeaderColumn: %d\", table->ResizedColumn, table->ReorderColumn, table->HeldHeaderColumn);\n    for (int n = 0; n < table->InstanceCurrent + 1; n++)\n    {\n        ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, n);\n        BulletText(\"Instance %d: HoveredRow: %d, LastOuterHeight: %.2f\", n, table_instance->HoveredRowLast, table_instance->LastOuterHeight);\n    }\n    //BulletText(\"BgDrawChannels: %d/%d\", 0, table->BgDrawChannelUnfrozen);\n    float sum_weights = 0.0f;\n    for (int n = 0; n < table->ColumnsCount; n++)\n        if (table->Columns[n].Flags & ImGuiTableColumnFlags_WidthStretch)\n            sum_weights += table->Columns[n].StretchWeight;\n    for (int n = 0; n < table->ColumnsCount; n++)\n    {\n        ImGuiTableColumn* column = &table->Columns[n];\n        const char* name = TableGetColumnName(table, n);\n        char buf[512];\n        ImFormatString(buf, IM_ARRAYSIZE(buf),\n            \"Column %d order %d '%s': offset %+.2f to %+.2f%s\\n\"\n            \"Enabled: %d, VisibleX/Y: %d/%d, RequestOutput: %d, SkipItems: %d, DrawChannels: %d,%d\\n\"\n            \"WidthGiven: %.1f, Request/Auto: %.1f/%.1f, StretchWeight: %.3f (%.1f%%)\\n\"\n            \"MinX: %.1f, MaxX: %.1f (%+.1f), ClipRect: %.1f to %.1f (+%.1f)\\n\"\n            \"ContentWidth: %.1f,%.1f, HeadersUsed/Ideal %.1f/%.1f\\n\"\n            \"Sort: %d%s, UserID: 0x%08X, Flags: 0x%04X: %s%s%s..\",\n            n, column->DisplayOrder, name, column->MinX - table->WorkRect.Min.x, column->MaxX - table->WorkRect.Min.x, (n < table->FreezeColumnsRequest) ? \" (Frozen)\" : \"\",\n            column->IsEnabled, column->IsVisibleX, column->IsVisibleY, column->IsRequestOutput, column->IsSkipItems, column->DrawChannelFrozen, column->DrawChannelUnfrozen,\n            column->WidthGiven, column->WidthRequest, column->WidthAuto, column->StretchWeight, column->StretchWeight > 0.0f ? (column->StretchWeight / sum_weights) * 100.0f : 0.0f,\n            column->MinX, column->MaxX, column->MaxX - column->MinX, column->ClipRect.Min.x, column->ClipRect.Max.x, column->ClipRect.Max.x - column->ClipRect.Min.x,\n            column->ContentMaxXFrozen - column->WorkMinX, column->ContentMaxXUnfrozen - column->WorkMinX, column->ContentMaxXHeadersUsed - column->WorkMinX, column->ContentMaxXHeadersIdeal - column->WorkMinX,\n            column->SortOrder, (column->SortDirection == ImGuiSortDirection_Ascending) ? \" (Asc)\" : (column->SortDirection == ImGuiSortDirection_Descending) ? \" (Des)\" : \"\", column->UserID, column->Flags,\n            (column->Flags & ImGuiTableColumnFlags_WidthStretch) ? \"WidthStretch \" : \"\",\n            (column->Flags & ImGuiTableColumnFlags_WidthFixed) ? \"WidthFixed \" : \"\",\n            (column->Flags & ImGuiTableColumnFlags_NoResize) ? \"NoResize \" : \"\");\n        Bullet();\n        Selectable(buf);\n        if (IsItemHovered())\n        {\n            ImRect r(column->MinX, table->OuterRect.Min.y, column->MaxX, table->OuterRect.Max.y);\n            GetForegroundDrawList()->AddRect(r.Min, r.Max, IM_COL32(255, 255, 0, 255));\n        }\n    }\n    if (ImGuiTableSettings* settings = TableGetBoundSettings(table))\n        DebugNodeTableSettings(settings);\n    if (clear_settings)\n        table->IsResetAllRequest = true;\n    TreePop();\n}\n\nvoid ImGui::DebugNodeTableSettings(ImGuiTableSettings* settings)\n{\n    if (!TreeNode((void*)(intptr_t)settings->ID, \"Settings 0x%08X (%d columns)\", settings->ID, settings->ColumnsCount))\n        return;\n    BulletText(\"SaveFlags: 0x%08X\", settings->SaveFlags);\n    BulletText(\"ColumnsCount: %d (max %d)\", settings->ColumnsCount, settings->ColumnsCountMax);\n    for (int n = 0; n < settings->ColumnsCount; n++)\n    {\n        ImGuiTableColumnSettings* column_settings = &settings->GetColumnSettings()[n];\n        ImGuiSortDirection sort_dir = (column_settings->SortOrder != -1) ? (ImGuiSortDirection)column_settings->SortDirection : ImGuiSortDirection_None;\n        BulletText(\"Column %d Order %d SortOrder %d %s Vis %d %s %7.3f UserID 0x%08X\",\n            n, column_settings->DisplayOrder, column_settings->SortOrder,\n            (sort_dir == ImGuiSortDirection_Ascending) ? \"Asc\" : (sort_dir == ImGuiSortDirection_Descending) ? \"Des\" : \"---\",\n            column_settings->IsEnabled, column_settings->IsStretch ? \"Weight\" : \"Width \", column_settings->WidthOrWeight, column_settings->UserID);\n    }\n    TreePop();\n}\n\n#else // #ifndef IMGUI_DISABLE_DEBUG_TOOLS\n\nvoid ImGui::DebugNodeTable(ImGuiTable*) {}\nvoid ImGui::DebugNodeTableSettings(ImGuiTableSettings*) {}\n\n#endif\n\n\n//-------------------------------------------------------------------------\n// [SECTION] Columns, BeginColumns, EndColumns, etc.\n// (This is a legacy API, prefer using BeginTable/EndTable!)\n//-------------------------------------------------------------------------\n// FIXME: sizing is lossy when columns width is very small (default width may turn negative etc.)\n//-------------------------------------------------------------------------\n// - SetWindowClipRectBeforeSetChannel() [Internal]\n// - GetColumnIndex()\n// - GetColumnsCount()\n// - GetColumnOffset()\n// - GetColumnWidth()\n// - SetColumnOffset()\n// - SetColumnWidth()\n// - PushColumnClipRect() [Internal]\n// - PushColumnsBackground() [Internal]\n// - PopColumnsBackground() [Internal]\n// - FindOrCreateColumns() [Internal]\n// - GetColumnsID() [Internal]\n// - BeginColumns()\n// - NextColumn()\n// - EndColumns()\n// - Columns()\n//-------------------------------------------------------------------------\n\n// [Internal] Small optimization to avoid calls to PopClipRect/SetCurrentChannel/PushClipRect in sequences,\n// they would meddle many times with the underlying ImDrawCmd.\n// Instead, we do a preemptive overwrite of clipping rectangle _without_ altering the command-buffer and let\n// the subsequent single call to SetCurrentChannel() does it things once.\nvoid ImGui::SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect)\n{\n    ImVec4 clip_rect_vec4 = clip_rect.ToVec4();\n    window->ClipRect = clip_rect;\n    window->DrawList->_CmdHeader.ClipRect = clip_rect_vec4;\n    window->DrawList->_ClipRectStack.Data[window->DrawList->_ClipRectStack.Size - 1] = clip_rect_vec4;\n}\n\nint ImGui::GetColumnIndex()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CurrentColumns ? window->DC.CurrentColumns->Current : 0;\n}\n\nint ImGui::GetColumnsCount()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    return window->DC.CurrentColumns ? window->DC.CurrentColumns->Count : 1;\n}\n\nfloat ImGui::GetColumnOffsetFromNorm(const ImGuiOldColumns* columns, float offset_norm)\n{\n    return offset_norm * (columns->OffMaxX - columns->OffMinX);\n}\n\nfloat ImGui::GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offset)\n{\n    return offset / (columns->OffMaxX - columns->OffMinX);\n}\n\nstatic const float COLUMNS_HIT_RECT_HALF_WIDTH = 4.0f;\n\nstatic float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index)\n{\n    // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing\n    // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning.\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.\n    IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));\n\n    float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + COLUMNS_HIT_RECT_HALF_WIDTH - window->Pos.x;\n    x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);\n    if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths))\n        x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);\n\n    return x;\n}\n\nfloat ImGui::GetColumnOffset(int column_index)\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (columns == NULL)\n        return 0.0f;\n\n    if (column_index < 0)\n        column_index = columns->Current;\n    IM_ASSERT(column_index < columns->Columns.Size);\n\n    const float t = columns->Columns[column_index].OffsetNorm;\n    const float x_offset = ImLerp(columns->OffMinX, columns->OffMaxX, t);\n    return x_offset;\n}\n\nstatic float GetColumnWidthEx(ImGuiOldColumns* columns, int column_index, bool before_resize = false)\n{\n    if (column_index < 0)\n        column_index = columns->Current;\n\n    float offset_norm;\n    if (before_resize)\n        offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize;\n    else\n        offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm;\n    return ImGui::GetColumnOffsetFromNorm(columns, offset_norm);\n}\n\nfloat ImGui::GetColumnWidth(int column_index)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (columns == NULL)\n        return GetContentRegionAvail().x;\n\n    if (column_index < 0)\n        column_index = columns->Current;\n    return GetColumnOffsetFromNorm(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm);\n}\n\nvoid ImGui::SetColumnOffset(int column_index, float offset)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    IM_ASSERT(columns != NULL);\n\n    if (column_index < 0)\n        column_index = columns->Current;\n    IM_ASSERT(column_index < columns->Columns.Size);\n\n    const bool preserve_width = !(columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths) && (column_index < columns->Count - 1);\n    const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f;\n\n    if (!(columns->Flags & ImGuiOldColumnFlags_NoForceWithinWindow))\n        offset = ImMin(offset, columns->OffMaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index));\n    columns->Columns[column_index].OffsetNorm = GetColumnNormFromOffset(columns, offset - columns->OffMinX);\n\n    if (preserve_width)\n        SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width));\n}\n\nvoid ImGui::SetColumnWidth(int column_index, float width)\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    IM_ASSERT(columns != NULL);\n\n    if (column_index < 0)\n        column_index = columns->Current;\n    SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width);\n}\n\nvoid ImGui::PushColumnClipRect(int column_index)\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (column_index < 0)\n        column_index = columns->Current;\n\n    ImGuiOldColumnData* column = &columns->Columns[column_index];\n    PushClipRect(column->ClipRect.Min, column->ClipRect.Max, false);\n}\n\n// Get into the columns background draw command (which is generally the same draw command as before we called BeginColumns)\nvoid ImGui::PushColumnsBackground()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (columns->Count == 1)\n        return;\n\n    // Optimization: avoid SetCurrentChannel() + PushClipRect()\n    columns->HostBackupClipRect = window->ClipRect;\n    SetWindowClipRectBeforeSetChannel(window, columns->HostInitialClipRect);\n    columns->Splitter.SetCurrentChannel(window->DrawList, 0);\n}\n\nvoid ImGui::PopColumnsBackground()\n{\n    ImGuiWindow* window = GetCurrentWindowRead();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (columns->Count == 1)\n        return;\n\n    // Optimization: avoid PopClipRect() + SetCurrentChannel()\n    SetWindowClipRectBeforeSetChannel(window, columns->HostBackupClipRect);\n    columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1);\n}\n\nImGuiOldColumns* ImGui::FindOrCreateColumns(ImGuiWindow* window, ImGuiID id)\n{\n    // We have few columns per window so for now we don't need bother much with turning this into a faster lookup.\n    for (int n = 0; n < window->ColumnsStorage.Size; n++)\n        if (window->ColumnsStorage[n].ID == id)\n            return &window->ColumnsStorage[n];\n\n    window->ColumnsStorage.push_back(ImGuiOldColumns());\n    ImGuiOldColumns* columns = &window->ColumnsStorage.back();\n    columns->ID = id;\n    return columns;\n}\n\nImGuiID ImGui::GetColumnsID(const char* str_id, int columns_count)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n\n    // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget.\n    // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer.\n    PushID(0x11223347 + (str_id ? 0 : columns_count));\n    ImGuiID id = window->GetID(str_id ? str_id : \"columns\");\n    PopID();\n\n    return id;\n}\n\nvoid ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiOldColumnFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n\n    IM_ASSERT(columns_count >= 1);\n    IM_ASSERT(window->DC.CurrentColumns == NULL);   // Nested columns are currently not supported\n\n    // Acquire storage for the columns set\n    ImGuiID id = GetColumnsID(str_id, columns_count);\n    ImGuiOldColumns* columns = FindOrCreateColumns(window, id);\n    IM_ASSERT(columns->ID == id);\n    columns->Current = 0;\n    columns->Count = columns_count;\n    columns->Flags = flags;\n    window->DC.CurrentColumns = columns;\n    window->DC.NavIsScrollPushableX = false; // Shortcut for NavUpdateCurrentWindowIsScrollPushableX();\n\n    columns->HostCursorPosY = window->DC.CursorPos.y;\n    columns->HostCursorMaxPosX = window->DC.CursorMaxPos.x;\n    columns->HostInitialClipRect = window->ClipRect;\n    columns->HostBackupParentWorkRect = window->ParentWorkRect;\n    window->ParentWorkRect = window->WorkRect;\n\n    // Set state for first column\n    // We aim so that the right-most column will have the same clipping width as other after being clipped by parent ClipRect\n    const float column_padding = g.Style.ItemSpacing.x;\n    const float half_clip_extend_x = ImTrunc(ImMax(window->WindowPadding.x * 0.5f, window->WindowBorderSize));\n    const float max_1 = window->WorkRect.Max.x + column_padding - ImMax(column_padding - window->WindowPadding.x, 0.0f);\n    const float max_2 = window->WorkRect.Max.x + half_clip_extend_x;\n    columns->OffMinX = window->DC.Indent.x - column_padding + ImMax(column_padding - window->WindowPadding.x, 0.0f);\n    columns->OffMaxX = ImMax(ImMin(max_1, max_2) - window->Pos.x, columns->OffMinX + 1.0f);\n    columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y;\n\n    // Clear data if columns count changed\n    if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1)\n        columns->Columns.resize(0);\n\n    // Initialize default widths\n    columns->IsFirstFrame = (columns->Columns.Size == 0);\n    if (columns->Columns.Size == 0)\n    {\n        columns->Columns.reserve(columns_count + 1);\n        for (int n = 0; n < columns_count + 1; n++)\n        {\n            ImGuiOldColumnData column;\n            column.OffsetNorm = n / (float)columns_count;\n            columns->Columns.push_back(column);\n        }\n    }\n\n    for (int n = 0; n < columns_count; n++)\n    {\n        // Compute clipping rectangle\n        ImGuiOldColumnData* column = &columns->Columns[n];\n        float clip_x1 = IM_ROUND(window->Pos.x + GetColumnOffset(n));\n        float clip_x2 = IM_ROUND(window->Pos.x + GetColumnOffset(n + 1) - 1.0f);\n        column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX);\n        column->ClipRect.ClipWithFull(window->ClipRect);\n    }\n\n    if (columns->Count > 1)\n    {\n        columns->Splitter.Split(window->DrawList, 1 + columns->Count);\n        columns->Splitter.SetCurrentChannel(window->DrawList, 1);\n        PushColumnClipRect(0);\n    }\n\n    // We don't generally store Indent.x inside ColumnsOffset because it may be manipulated by the user.\n    float offset_0 = GetColumnOffset(columns->Current);\n    float offset_1 = GetColumnOffset(columns->Current + 1);\n    float width = offset_1 - offset_0;\n    PushItemWidth(width * 0.65f);\n    window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f);\n    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);\n    window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding;\n    window->WorkRect.Max.y = window->ContentRegionRect.Max.y;\n}\n\nvoid ImGui::NextColumn()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems || window->DC.CurrentColumns == NULL)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n\n    if (columns->Count == 1)\n    {\n        window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);\n        IM_ASSERT(columns->Current == 0);\n        return;\n    }\n\n    // Next column\n    if (++columns->Current == columns->Count)\n        columns->Current = 0;\n\n    PopItemWidth();\n\n    // Optimization: avoid PopClipRect() + SetCurrentChannel() + PushClipRect()\n    // (which would needlessly attempt to update commands in the wrong channel, then pop or overwrite them),\n    ImGuiOldColumnData* column = &columns->Columns[columns->Current];\n    SetWindowClipRectBeforeSetChannel(window, column->ClipRect);\n    columns->Splitter.SetCurrentChannel(window->DrawList, columns->Current + 1);\n\n    const float column_padding = g.Style.ItemSpacing.x;\n    columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);\n    if (columns->Current > 0)\n    {\n        // Columns 1+ ignore IndentX (by canceling it out)\n        // FIXME-COLUMNS: Unnecessary, could be locked?\n        window->DC.ColumnsOffset.x = GetColumnOffset(columns->Current) - window->DC.Indent.x + column_padding;\n    }\n    else\n    {\n        // New row/line: column 0 honor IndentX.\n        window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f);\n        window->DC.IsSameLine = false;\n        columns->LineMinY = columns->LineMaxY;\n    }\n    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);\n    window->DC.CursorPos.y = columns->LineMinY;\n    window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);\n    window->DC.CurrLineTextBaseOffset = 0.0f;\n\n    // FIXME-COLUMNS: Share code with BeginColumns() - move code on columns setup.\n    float offset_0 = GetColumnOffset(columns->Current);\n    float offset_1 = GetColumnOffset(columns->Current + 1);\n    float width = offset_1 - offset_0;\n    PushItemWidth(width * 0.65f);\n    window->WorkRect.Max.x = window->Pos.x + offset_1 - column_padding;\n}\n\nvoid ImGui::EndColumns()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    IM_ASSERT(columns != NULL);\n\n    PopItemWidth();\n    if (columns->Count > 1)\n    {\n        PopClipRect();\n        columns->Splitter.Merge(window->DrawList);\n    }\n\n    const ImGuiOldColumnFlags flags = columns->Flags;\n    columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y);\n    window->DC.CursorPos.y = columns->LineMaxY;\n    if (!(flags & ImGuiOldColumnFlags_GrowParentContentsSize))\n        window->DC.CursorMaxPos.x = columns->HostCursorMaxPosX;  // Restore cursor max pos, as columns don't grow parent\n\n    // Draw columns borders and handle resize\n    // The IsBeingResized flag ensure we preserve pre-resize columns width so back-and-forth are not lossy\n    bool is_being_resized = false;\n    if (!(flags & ImGuiOldColumnFlags_NoBorder) && !window->SkipItems)\n    {\n        // We clip Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.\n        const float y1 = ImMax(columns->HostCursorPosY, window->ClipRect.Min.y);\n        const float y2 = ImMin(window->DC.CursorPos.y, window->ClipRect.Max.y);\n        int dragging_column = -1;\n        for (int n = 1; n < columns->Count; n++)\n        {\n            ImGuiOldColumnData* column = &columns->Columns[n];\n            float x = window->Pos.x + GetColumnOffset(n);\n            const ImGuiID column_id = columns->ID + ImGuiID(n);\n            const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH;\n            const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2));\n            if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav))\n                continue;\n\n            bool hovered = false, held = false;\n            if (!(flags & ImGuiOldColumnFlags_NoResize))\n            {\n                ButtonBehavior(column_hit_rect, column_id, &hovered, &held);\n                if (hovered || held)\n                    g.MouseCursor = ImGuiMouseCursor_ResizeEW;\n                if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize))\n                    dragging_column = n;\n            }\n\n            // Draw column\n            const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);\n            const float xi = IM_TRUNC(x);\n            window->DrawList->AddLine(ImVec2(xi, y1 + 1.0f), ImVec2(xi, y2), col);\n        }\n\n        // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame.\n        if (dragging_column != -1)\n        {\n            if (!columns->IsBeingResized)\n                for (int n = 0; n < columns->Count + 1; n++)\n                    columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm;\n            columns->IsBeingResized = is_being_resized = true;\n            float x = GetDraggedColumnOffset(columns, dragging_column);\n            SetColumnOffset(dragging_column, x);\n        }\n    }\n    columns->IsBeingResized = is_being_resized;\n\n    window->WorkRect = window->ParentWorkRect;\n    window->ParentWorkRect = columns->HostBackupParentWorkRect;\n    window->DC.CurrentColumns = NULL;\n    window->DC.ColumnsOffset.x = 0.0f;\n    window->DC.CursorPos.x = IM_TRUNC(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x);\n    NavUpdateCurrentWindowIsScrollPushableX();\n}\n\nvoid ImGui::Columns(int columns_count, const char* id, bool border)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    IM_ASSERT(columns_count >= 1);\n\n    ImGuiOldColumnFlags flags = (border ? 0 : ImGuiOldColumnFlags_NoBorder);\n    //flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior\n    ImGuiOldColumns* columns = window->DC.CurrentColumns;\n    if (columns != NULL && columns->Count == columns_count && columns->Flags == flags)\n        return;\n\n    if (columns != NULL)\n        EndColumns();\n\n    if (columns_count != 1)\n        BeginColumns(id, columns_count, flags);\n}\n\n//-------------------------------------------------------------------------\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "third-party/imgui/imgui_widgets.cpp",
    "content": "// dear imgui, v1.90.0\n// (widgets code)\n\n/*\n\nIndex of this file:\n\n// [SECTION] Forward Declarations\n// [SECTION] Widgets: Text, etc.\n// [SECTION] Widgets: Main (Button, Image, Checkbox, RadioButton, ProgressBar, Bullet, etc.)\n// [SECTION] Widgets: Low-level Layout helpers (Spacing, Dummy, NewLine, Separator, etc.)\n// [SECTION] Widgets: ComboBox\n// [SECTION] Data Type and Data Formatting Helpers\n// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.\n// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.\n// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.\n// [SECTION] Widgets: InputText, InputTextMultiline\n// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.\n// [SECTION] Widgets: TreeNode, CollapsingHeader, etc.\n// [SECTION] Widgets: Selectable\n// [SECTION] Widgets: Typing-Select support\n// [SECTION] Widgets: Multi-Select support\n// [SECTION] Widgets: ListBox\n// [SECTION] Widgets: PlotLines, PlotHistogram\n// [SECTION] Widgets: Value helpers\n// [SECTION] Widgets: MenuItem, BeginMenu, EndMenu, etc.\n// [SECTION] Widgets: BeginTabBar, EndTabBar, etc.\n// [SECTION] Widgets: BeginTabItem, EndTabItem, etc.\n// [SECTION] Widgets: Columns, BeginColumns, EndColumns, etc.\n\n*/\n\n#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)\n#define _CRT_SECURE_NO_WARNINGS\n#endif\n\n#ifndef IMGUI_DEFINE_MATH_OPERATORS\n#define IMGUI_DEFINE_MATH_OPERATORS\n#endif\n\n#include \"imgui.h\"\n#ifndef IMGUI_DISABLE\n#include \"imgui_internal.h\"\n\n// System includes\n#include <stdint.h>     // intptr_t\n\n//-------------------------------------------------------------------------\n// Warnings\n//-------------------------------------------------------------------------\n\n// Visual Studio warnings\n#ifdef _MSC_VER\n#pragma warning (disable: 4127)     // condition expression is constant\n#pragma warning (disable: 4996)     // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen\n#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later\n#pragma warning (disable: 5054)     // operator '|': deprecated between enumerations of different types\n#endif\n#pragma warning (disable: 26451)    // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).\n#pragma warning (disable: 26812)    // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).\n#endif\n\n// Clang/GCC warnings with -Weverything\n#if defined(__clang__)\n#if __has_warning(\"-Wunknown-warning-option\")\n#pragma clang diagnostic ignored \"-Wunknown-warning-option\"         // warning: unknown warning group 'xxx'                      // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great!\n#endif\n#pragma clang diagnostic ignored \"-Wunknown-pragmas\"                // warning: unknown warning group 'xxx'\n#pragma clang diagnostic ignored \"-Wold-style-cast\"                 // warning: use of old-style cast                            // yes, they are more terse.\n#pragma clang diagnostic ignored \"-Wfloat-equal\"                    // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.\n#pragma clang diagnostic ignored \"-Wformat-nonliteral\"              // warning: format string is not a string literal            // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.\n#pragma clang diagnostic ignored \"-Wsign-conversion\"                // warning: implicit conversion changes signedness\n#pragma clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"  // warning: zero as null pointer constant                    // some standard header variations use #define NULL 0\n#pragma clang diagnostic ignored \"-Wdouble-promotion\"               // warning: implicit conversion from 'float' to 'double' when passing argument to function  // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.\n#pragma clang diagnostic ignored \"-Wenum-enum-conversion\"           // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')\n#pragma clang diagnostic ignored \"-Wdeprecated-enum-enum-conversion\"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated\n#pragma clang diagnostic ignored \"-Wimplicit-int-float-conversion\"  // warning: implicit conversion from 'xxx' to 'float' may lose precision\n#elif defined(__GNUC__)\n#pragma GCC diagnostic ignored \"-Wpragmas\"                          // warning: unknown option after '#pragma GCC diagnostic' kind\n#pragma GCC diagnostic ignored \"-Wformat-nonliteral\"                // warning: format not a string literal, format string not checked\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"                  // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead\n#pragma GCC diagnostic ignored \"-Wdeprecated-enum-enum-conversion\"  // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated\n#endif\n\n//-------------------------------------------------------------------------\n// Data\n//-------------------------------------------------------------------------\n\n// Widgets\nstatic const float          DRAGDROP_HOLD_TO_OPEN_TIMER = 0.70f;    // Time for drag-hold to activate items accepting the ImGuiButtonFlags_PressedOnDragDropHold button behavior.\nstatic const float          DRAG_MOUSE_THRESHOLD_FACTOR = 0.50f;    // Multiplier for the default value of io.MouseDragThreshold to make DragFloat/DragInt react faster to mouse drags.\n\n// Those MIN/MAX values are not define because we need to point to them\nstatic const signed char    IM_S8_MIN  = -128;\nstatic const signed char    IM_S8_MAX  = 127;\nstatic const unsigned char  IM_U8_MIN  = 0;\nstatic const unsigned char  IM_U8_MAX  = 0xFF;\nstatic const signed short   IM_S16_MIN = -32768;\nstatic const signed short   IM_S16_MAX = 32767;\nstatic const unsigned short IM_U16_MIN = 0;\nstatic const unsigned short IM_U16_MAX = 0xFFFF;\nstatic const ImS32          IM_S32_MIN = INT_MIN;    // (-2147483647 - 1), (0x80000000);\nstatic const ImS32          IM_S32_MAX = INT_MAX;    // (2147483647), (0x7FFFFFFF)\nstatic const ImU32          IM_U32_MIN = 0;\nstatic const ImU32          IM_U32_MAX = UINT_MAX;   // (0xFFFFFFFF)\n#ifdef LLONG_MIN\nstatic const ImS64          IM_S64_MIN = LLONG_MIN;  // (-9223372036854775807ll - 1ll);\nstatic const ImS64          IM_S64_MAX = LLONG_MAX;  // (9223372036854775807ll);\n#else\nstatic const ImS64          IM_S64_MIN = -9223372036854775807LL - 1;\nstatic const ImS64          IM_S64_MAX = 9223372036854775807LL;\n#endif\nstatic const ImU64          IM_U64_MIN = 0;\n#ifdef ULLONG_MAX\nstatic const ImU64          IM_U64_MAX = ULLONG_MAX; // (0xFFFFFFFFFFFFFFFFull);\n#else\nstatic const ImU64          IM_U64_MAX = (2ULL * 9223372036854775807LL + 1);\n#endif\n\n//-------------------------------------------------------------------------\n// [SECTION] Forward Declarations\n//-------------------------------------------------------------------------\n\n// For InputTextEx()\nstatic bool             InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source);\nstatic int              InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end);\nstatic ImVec2           InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false);\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Text, etc.\n//-------------------------------------------------------------------------\n// - TextEx() [Internal]\n// - TextUnformatted()\n// - Text()\n// - TextV()\n// - TextColored()\n// - TextColoredV()\n// - TextDisabled()\n// - TextDisabledV()\n// - TextWrapped()\n// - TextWrappedV()\n// - LabelText()\n// - LabelTextV()\n// - BulletText()\n// - BulletTextV()\n//-------------------------------------------------------------------------\n\nvoid ImGui::TextEx(const char* text, const char* text_end, ImGuiTextFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n    ImGuiContext& g = *GImGui;\n\n    // Accept null ranges\n    if (text == text_end)\n        text = text_end = \"\";\n\n    // Calculate length\n    const char* text_begin = text;\n    if (text_end == NULL)\n        text_end = text + strlen(text); // FIXME-OPT\n\n    const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);\n    const float wrap_pos_x = window->DC.TextWrapPos;\n    const bool wrap_enabled = (wrap_pos_x >= 0.0f);\n    if (text_end - text <= 2000 || wrap_enabled)\n    {\n        // Common case\n        const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f;\n        const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);\n\n        ImRect bb(text_pos, text_pos + text_size);\n        ItemSize(text_size, 0.0f);\n        if (!ItemAdd(bb, 0))\n            return;\n\n        // Render (we don't hide text after ## in this end-user function)\n        RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width);\n    }\n    else\n    {\n        // Long text!\n        // Perform manual coarse clipping to optimize for long multi-line text\n        // - From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled.\n        // - We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line.\n        // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster than a casually written loop.\n        const char* line = text;\n        const float line_height = GetTextLineHeight();\n        ImVec2 text_size(0, 0);\n\n        // Lines to skip (can't skip when logging text)\n        ImVec2 pos = text_pos;\n        if (!g.LogEnabled)\n        {\n            int lines_skippable = (int)((window->ClipRect.Min.y - text_pos.y) / line_height);\n            if (lines_skippable > 0)\n            {\n                int lines_skipped = 0;\n                while (line < text_end && lines_skipped < lines_skippable)\n                {\n                    const char* line_end = (const char*)memchr(line, '\\n', text_end - line);\n                    if (!line_end)\n                        line_end = text_end;\n                    if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)\n                        text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);\n                    line = line_end + 1;\n                    lines_skipped++;\n                }\n                pos.y += lines_skipped * line_height;\n            }\n        }\n\n        // Lines to render\n        if (line < text_end)\n        {\n            ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));\n            while (line < text_end)\n            {\n                if (IsClippedEx(line_rect, 0))\n                    break;\n\n                const char* line_end = (const char*)memchr(line, '\\n', text_end - line);\n                if (!line_end)\n                    line_end = text_end;\n                text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);\n                RenderText(pos, line, line_end, false);\n                line = line_end + 1;\n                line_rect.Min.y += line_height;\n                line_rect.Max.y += line_height;\n                pos.y += line_height;\n            }\n\n            // Count remaining lines\n            int lines_skipped = 0;\n            while (line < text_end)\n            {\n                const char* line_end = (const char*)memchr(line, '\\n', text_end - line);\n                if (!line_end)\n                    line_end = text_end;\n                if ((flags & ImGuiTextFlags_NoWidthForLargeClippedText) == 0)\n                    text_size.x = ImMax(text_size.x, CalcTextSize(line, line_end).x);\n                line = line_end + 1;\n                lines_skipped++;\n            }\n            pos.y += lines_skipped * line_height;\n        }\n        text_size.y = (pos - text_pos).y;\n\n        ImRect bb(text_pos, text_pos + text_size);\n        ItemSize(text_size, 0.0f);\n        ItemAdd(bb, 0);\n    }\n}\n\nvoid ImGui::TextUnformatted(const char* text, const char* text_end)\n{\n    TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);\n}\n\nvoid ImGui::Text(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    TextV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::TextV(const char* fmt, va_list args)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    const char* text, *text_end;\n    ImFormatStringToTempBufferV(&text, &text_end, fmt, args);\n    TextEx(text, text_end, ImGuiTextFlags_NoWidthForLargeClippedText);\n}\n\nvoid ImGui::TextColored(const ImVec4& col, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    TextColoredV(col, fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args)\n{\n    PushStyleColor(ImGuiCol_Text, col);\n    TextV(fmt, args);\n    PopStyleColor();\n}\n\nvoid ImGui::TextDisabled(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    TextDisabledV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::TextDisabledV(const char* fmt, va_list args)\n{\n    ImGuiContext& g = *GImGui;\n    PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);\n    TextV(fmt, args);\n    PopStyleColor();\n}\n\nvoid ImGui::TextWrapped(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    TextWrappedV(fmt, args);\n    va_end(args);\n}\n\nvoid ImGui::TextWrappedV(const char* fmt, va_list args)\n{\n    ImGuiContext& g = *GImGui;\n    const bool need_backup = (g.CurrentWindow->DC.TextWrapPos < 0.0f);  // Keep existing wrap position if one is already set\n    if (need_backup)\n        PushTextWrapPos(0.0f);\n    TextV(fmt, args);\n    if (need_backup)\n        PopTextWrapPos();\n}\n\nvoid ImGui::LabelText(const char* label, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    LabelTextV(label, fmt, args);\n    va_end(args);\n}\n\n// Add a label+text combo aligned to other label+value widgets\nvoid ImGui::LabelTextV(const char* label, const char* fmt, va_list args)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const float w = CalcItemWidth();\n\n    const char* value_text_begin, *value_text_end;\n    ImFormatStringToTempBufferV(&value_text_begin, &value_text_end, fmt, args);\n    const ImVec2 value_size = CalcTextSize(value_text_begin, value_text_end, false);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    const ImVec2 pos = window->DC.CursorPos;\n    const ImRect value_bb(pos, pos + ImVec2(w, value_size.y + style.FramePadding.y * 2));\n    const ImRect total_bb(pos, pos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), ImMax(value_size.y, label_size.y) + style.FramePadding.y * 2));\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, 0))\n        return;\n\n    // Render\n    RenderTextClipped(value_bb.Min + style.FramePadding, value_bb.Max, value_text_begin, value_text_end, &value_size, ImVec2(0.0f, 0.0f));\n    if (label_size.x > 0.0f)\n        RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label);\n}\n\nvoid ImGui::BulletText(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    BulletTextV(fmt, args);\n    va_end(args);\n}\n\n// Text with a little bullet aligned to the typical tree node.\nvoid ImGui::BulletTextV(const char* fmt, va_list args)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n\n    const char* text_begin, *text_end;\n    ImFormatStringToTempBufferV(&text_begin, &text_end, fmt, args);\n    const ImVec2 label_size = CalcTextSize(text_begin, text_end, false);\n    const ImVec2 total_size = ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x * 2) : 0.0f), label_size.y);  // Empty text doesn't add padding\n    ImVec2 pos = window->DC.CursorPos;\n    pos.y += window->DC.CurrLineTextBaseOffset;\n    ItemSize(total_size, 0.0f);\n    const ImRect bb(pos, pos + total_size);\n    if (!ItemAdd(bb, 0))\n        return;\n\n    // Render\n    ImU32 text_col = GetColorU32(ImGuiCol_Text);\n    RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, g.FontSize * 0.5f), text_col);\n    RenderText(bb.Min + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0f), text_begin, text_end, false);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Main\n//-------------------------------------------------------------------------\n// - ButtonBehavior() [Internal]\n// - Button()\n// - SmallButton()\n// - InvisibleButton()\n// - ArrowButton()\n// - CloseButton() [Internal]\n// - CollapseButton() [Internal]\n// - GetWindowScrollbarID() [Internal]\n// - GetWindowScrollbarRect() [Internal]\n// - Scrollbar() [Internal]\n// - ScrollbarEx() [Internal]\n// - Image()\n// - ImageButton()\n// - Checkbox()\n// - CheckboxFlagsT() [Internal]\n// - CheckboxFlags()\n// - RadioButton()\n// - ProgressBar()\n// - Bullet()\n//-------------------------------------------------------------------------\n\n// The ButtonBehavior() function is key to many interactions and used by many/most widgets.\n// Because we handle so many cases (keyboard/gamepad navigation, drag and drop) and many specific behavior (via ImGuiButtonFlags_),\n// this code is a little complex.\n// By far the most common path is interacting with the Mouse using the default ImGuiButtonFlags_PressedOnClickRelease button behavior.\n// See the series of events below and the corresponding state reported by dear imgui:\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// with PressedOnClickRelease:             return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()\n//   Frame N+0 (mouse is outside bb)        -             -                -               -                  -                    -\n//   Frame N+1 (mouse moves inside bb)      -             true             -               -                  -                    -\n//   Frame N+2 (mouse button is down)       -             true             true            true               -                    true\n//   Frame N+3 (mouse button is down)       -             true             true            -                  -                    -\n//   Frame N+4 (mouse moves outside bb)     -             -                true            -                  -                    -\n//   Frame N+5 (mouse moves inside bb)      -             true             true            -                  -                    -\n//   Frame N+6 (mouse button is released)   true          true             -               -                  true                 -\n//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -\n//   Frame N+8 (mouse moves outside bb)     -             -                -               -                  -                    -\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// with PressedOnClick:                    return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()\n//   Frame N+2 (mouse button is down)       true          true             true            true               -                    true\n//   Frame N+3 (mouse button is down)       -             true             true            -                  -                    -\n//   Frame N+6 (mouse button is released)   -             true             -               -                  true                 -\n//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// with PressedOnRelease:                  return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()\n//   Frame N+2 (mouse button is down)       -             true             -               -                  -                    true\n//   Frame N+3 (mouse button is down)       -             true             -               -                  -                    -\n//   Frame N+6 (mouse button is released)   true          true             -               -                  -                    -\n//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// with PressedOnDoubleClick:              return-value  IsItemHovered()  IsItemActive()  IsItemActivated()  IsItemDeactivated()  IsItemClicked()\n//   Frame N+0 (mouse button is down)       -             true             -               -                  -                    true\n//   Frame N+1 (mouse button is down)       -             true             -               -                  -                    -\n//   Frame N+2 (mouse button is released)   -             true             -               -                  -                    -\n//   Frame N+3 (mouse button is released)   -             true             -               -                  -                    -\n//   Frame N+4 (mouse button is down)       true          true             true            true               -                    true\n//   Frame N+5 (mouse button is down)       -             true             true            -                  -                    -\n//   Frame N+6 (mouse button is released)   -             true             -               -                  true                 -\n//   Frame N+7 (mouse button is released)   -             true             -               -                  -                    -\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// Note that some combinations are supported,\n// - PressedOnDragDropHold can generally be associated with any flag.\n// - PressedOnDoubleClick can be associated by PressedOnClickRelease/PressedOnRelease, in which case the second release event won't be reported.\n//------------------------------------------------------------------------------------------------------------------------------------------------\n// The behavior of the return-value changes when ImGuiButtonFlags_Repeat is set:\n//                                         Repeat+                  Repeat+           Repeat+             Repeat+\n//                                         PressedOnClickRelease    PressedOnClick    PressedOnRelease    PressedOnDoubleClick\n//-------------------------------------------------------------------------------------------------------------------------------------------------\n//   Frame N+0 (mouse button is down)       -                        true              -                   true\n//   ...                                    -                        -                 -                   -\n//   Frame N + RepeatDelay                  true                     true              -                   true\n//   ...                                    -                        -                 -                   -\n//   Frame N + RepeatDelay + RepeatRate*N   true                     true              -                   true\n//-------------------------------------------------------------------------------------------------------------------------------------------------\n\nbool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n\n    // Default only reacts to left mouse button\n    if ((flags & ImGuiButtonFlags_MouseButtonMask_) == 0)\n        flags |= ImGuiButtonFlags_MouseButtonDefault_;\n\n    // Default behavior requires click + release inside bounding box\n    if ((flags & ImGuiButtonFlags_PressedOnMask_) == 0)\n        flags |= ImGuiButtonFlags_PressedOnDefault_;\n\n    // Default behavior inherited from item flags\n    // Note that _both_ ButtonFlags and ItemFlags are valid sources, so copy one into the item_flags and only check that.\n    ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags);\n    if (flags & ImGuiButtonFlags_AllowOverlap)\n        item_flags |= ImGuiItemFlags_AllowOverlap;\n    if (flags & ImGuiButtonFlags_Repeat)\n        item_flags |= ImGuiItemFlags_ButtonRepeat;\n\n    ImGuiWindow* backup_hovered_window = g.HoveredWindow;\n    const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window;\n    if (flatten_hovered_children)\n        g.HoveredWindow = window;\n\n#ifdef IMGUI_ENABLE_TEST_ENGINE\n    // Alternate registration spot, for when caller didn't use ItemAdd()\n    if (id != 0 && g.LastItemData.ID != id)\n        IMGUI_TEST_ENGINE_ITEM_ADD(id, bb, NULL);\n#endif\n\n    bool pressed = false;\n    bool hovered = ItemHoverable(bb, id, item_flags);\n\n    // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button\n    if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers))\n        if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))\n        {\n            hovered = true;\n            SetHoveredID(id);\n            if (g.HoveredIdTimer - g.IO.DeltaTime <= DRAGDROP_HOLD_TO_OPEN_TIMER && g.HoveredIdTimer >= DRAGDROP_HOLD_TO_OPEN_TIMER)\n            {\n                pressed = true;\n                g.DragDropHoldJustPressedId = id;\n                FocusWindow(window);\n            }\n        }\n\n    if (flatten_hovered_children)\n        g.HoveredWindow = backup_hovered_window;\n\n    // Mouse handling\n    const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id;\n    if (hovered)\n    {\n        // Poll mouse buttons\n        // - 'mouse_button_clicked' is generally carried into ActiveIdMouseButton when setting ActiveId.\n        // - Technically we only need some values in one code path, but since this is gated by hovered test this is fine.\n        int mouse_button_clicked = -1;\n        int mouse_button_released = -1;\n        for (int button = 0; button < 3; button++)\n            if (flags & (ImGuiButtonFlags_MouseButtonLeft << button)) // Handle ImGuiButtonFlags_MouseButtonRight and ImGuiButtonFlags_MouseButtonMiddle here.\n            {\n                if (IsMouseClicked(button, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; }\n                if (IsMouseReleased(button, test_owner_id) && mouse_button_released == -1) { mouse_button_released = button; }\n            }\n\n        // Process initial action\n        if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt))\n        {\n            if (mouse_button_clicked != -1 && g.ActiveId != id)\n            {\n                if (!(flags & ImGuiButtonFlags_NoSetKeyOwner))\n                    SetKeyOwner(MouseButtonToKey(mouse_button_clicked), id);\n                if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere))\n                {\n                    SetActiveID(id, window);\n                    g.ActiveIdMouseButton = mouse_button_clicked;\n                    if (!(flags & ImGuiButtonFlags_NoNavFocus))\n                        SetFocusID(id, window);\n                    FocusWindow(window);\n                }\n                if ((flags & ImGuiButtonFlags_PressedOnClick) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseClickedCount[mouse_button_clicked] == 2))\n                {\n                    pressed = true;\n                    if (flags & ImGuiButtonFlags_NoHoldingActiveId)\n                        ClearActiveID();\n                    else\n                        SetActiveID(id, window); // Hold on ID\n                    if (!(flags & ImGuiButtonFlags_NoNavFocus))\n                        SetFocusID(id, window);\n                    g.ActiveIdMouseButton = mouse_button_clicked;\n                    FocusWindow(window);\n                }\n            }\n            if (flags & ImGuiButtonFlags_PressedOnRelease)\n            {\n                if (mouse_button_released != -1)\n                {\n                    const bool has_repeated_at_least_once = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior\n                    if (!has_repeated_at_least_once)\n                        pressed = true;\n                    if (!(flags & ImGuiButtonFlags_NoNavFocus))\n                        SetFocusID(id, window);\n                    ClearActiveID();\n                }\n            }\n\n            // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above).\n            // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings.\n            if (g.ActiveId == id && (item_flags & ImGuiItemFlags_ButtonRepeat))\n                if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, test_owner_id, ImGuiInputFlags_Repeat))\n                    pressed = true;\n        }\n\n        if (pressed)\n            g.NavDisableHighlight = true;\n    }\n\n    // Gamepad/Keyboard navigation\n    // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse.\n    if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId))\n        if (!(flags & ImGuiButtonFlags_NoHoveredOnFocus))\n            hovered = true;\n    if (g.NavActivateDownId == id)\n    {\n        bool nav_activated_by_code = (g.NavActivateId == id);\n        bool nav_activated_by_inputs = (g.NavActivatePressedId == id);\n        if (!nav_activated_by_inputs && (item_flags & ImGuiItemFlags_ButtonRepeat))\n        {\n            // Avoid pressing multiple keys from triggering excessive amount of repeat events\n            const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space);\n            const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_Enter);\n            const ImGuiKeyData* key3 = GetKeyData(ImGuiKey_NavGamepadActivate);\n            const float t1 = ImMax(ImMax(key1->DownDuration, key2->DownDuration), key3->DownDuration);\n            nav_activated_by_inputs = CalcTypematicRepeatAmount(t1 - g.IO.DeltaTime, t1, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0;\n        }\n        if (nav_activated_by_code || nav_activated_by_inputs)\n        {\n            // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button.\n            pressed = true;\n            SetActiveID(id, window);\n            g.ActiveIdSource = g.NavInputSource;\n            if (!(flags & ImGuiButtonFlags_NoNavFocus))\n                SetFocusID(id, window);\n        }\n    }\n\n    // Process while held\n    bool held = false;\n    if (g.ActiveId == id)\n    {\n        if (g.ActiveIdSource == ImGuiInputSource_Mouse)\n        {\n            if (g.ActiveIdIsJustActivated)\n                g.ActiveIdClickOffset = g.IO.MousePos - bb.Min;\n\n            const int mouse_button = g.ActiveIdMouseButton;\n            if (mouse_button == -1)\n            {\n                // Fallback for the rare situation were g.ActiveId was set programmatically or from another widget (e.g. #6304).\n                ClearActiveID();\n            }\n            else if (IsMouseDown(mouse_button, test_owner_id))\n            {\n                held = true;\n            }\n            else\n            {\n                bool release_in = hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease) != 0;\n                bool release_anywhere = (flags & ImGuiButtonFlags_PressedOnClickReleaseAnywhere) != 0;\n                if ((release_in || release_anywhere) && !g.DragDropActive)\n                {\n                    // Report as pressed when releasing the mouse (this is the most common path)\n                    bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2;\n                    bool is_repeating_already = (item_flags & ImGuiItemFlags_ButtonRepeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps <on release>\n                    bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id);\n                    if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned)\n                        pressed = true;\n                }\n                ClearActiveID();\n            }\n            if (!(flags & ImGuiButtonFlags_NoNavFocus))\n                g.NavDisableHighlight = true;\n        }\n        else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)\n        {\n            // When activated using Nav, we hold on the ActiveID until activation button is released\n            if (g.NavActivateDownId != id)\n                ClearActiveID();\n        }\n        if (pressed)\n            g.ActiveIdHasBeenPressedBefore = true;\n    }\n\n    if (out_hovered) *out_hovered = hovered;\n    if (out_held) *out_held = held;\n\n    return pressed;\n}\n\nbool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    ImVec2 pos = window->DC.CursorPos;\n    if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)\n        pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y;\n    ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f);\n\n    const ImRect bb(pos, pos + size);\n    ItemSize(size, style.FramePadding.y);\n    if (!ItemAdd(bb, id))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n    // Render\n    const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n    RenderNavHighlight(bb, id);\n    RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding);\n\n    if (g.LogEnabled)\n        LogSetNextTextDecoration(\"[\", \"]\");\n    RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb);\n\n    // Automatically close popups\n    //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup))\n    //    CloseCurrentPopup();\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n    return pressed;\n}\n\nbool ImGui::Button(const char* label, const ImVec2& size_arg)\n{\n    return ButtonEx(label, size_arg, ImGuiButtonFlags_None);\n}\n\n// Small buttons fits within text without additional vertical spacing.\nbool ImGui::SmallButton(const char* label)\n{\n    ImGuiContext& g = *GImGui;\n    float backup_padding_y = g.Style.FramePadding.y;\n    g.Style.FramePadding.y = 0.0f;\n    bool pressed = ButtonEx(label, ImVec2(0, 0), ImGuiButtonFlags_AlignTextBaseLine);\n    g.Style.FramePadding.y = backup_padding_y;\n    return pressed;\n}\n\n// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack.\n// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id)\nbool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg, ImGuiButtonFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    // Cannot use zero-size for InvisibleButton(). Unlike Button() there is not way to fallback using the label size.\n    IM_ASSERT(size_arg.x != 0.0f && size_arg.y != 0.0f);\n\n    const ImGuiID id = window->GetID(str_id);\n    ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f);\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);\n    ItemSize(size);\n    if (!ItemAdd(bb, id))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);\n    return pressed;\n}\n\nbool ImGui::ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size, ImGuiButtonFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    const ImGuiID id = window->GetID(str_id);\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);\n    const float default_size = GetFrameHeight();\n    ItemSize(size, (size.y >= default_size) ? g.Style.FramePadding.y : -1.0f);\n    if (!ItemAdd(bb, id))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n    // Render\n    const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n    const ImU32 text_col = GetColorU32(ImGuiCol_Text);\n    RenderNavHighlight(bb, id);\n    RenderFrame(bb.Min, bb.Max, bg_col, true, g.Style.FrameRounding);\n    RenderArrow(window->DrawList, bb.Min + ImVec2(ImMax(0.0f, (size.x - g.FontSize) * 0.5f), ImMax(0.0f, (size.y - g.FontSize) * 0.5f)), text_col, dir);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, str_id, g.LastItemData.StatusFlags);\n    return pressed;\n}\n\nbool ImGui::ArrowButton(const char* str_id, ImGuiDir dir)\n{\n    float sz = GetFrameHeight();\n    return ArrowButtonEx(str_id, dir, ImVec2(sz, sz), ImGuiButtonFlags_None);\n}\n\n// Button to close a window\nbool ImGui::CloseButton(ImGuiID id, const ImVec2& pos)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // Tweak 1: Shrink hit-testing area if button covers an abnormally large proportion of the visible region. That's in order to facilitate moving the window away. (#3825)\n    // This may better be applied as a general hit-rect reduction mechanism for all widgets to ensure the area to move window is always accessible?\n    const ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize));\n    ImRect bb_interact = bb;\n    const float area_to_visible_ratio = window->OuterRectClipped.GetArea() / bb.GetArea();\n    if (area_to_visible_ratio < 1.5f)\n        bb_interact.Expand(ImTrunc(bb_interact.GetSize() * -0.25f));\n\n    // Tweak 2: We intentionally allow interaction when clipped so that a mechanical Alt,Right,Activate sequence can always close a window.\n    // (this isn't the common behavior of buttons, but it doesn't affect the user because navigation tends to keep items visible in scrolling layer).\n    bool is_clipped = !ItemAdd(bb_interact, id);\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb_interact, id, &hovered, &held);\n    if (is_clipped)\n        return pressed;\n\n    // Render\n    // FIXME: Clarify this mess\n    ImU32 col = GetColorU32(held ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered);\n    ImVec2 center = bb.GetCenter();\n    if (hovered)\n        window->DrawList->AddCircleFilled(center, ImMax(2.0f, g.FontSize * 0.5f + 1.0f), col);\n\n    float cross_extent = g.FontSize * 0.5f * 0.7071f - 1.0f;\n    ImU32 cross_col = GetColorU32(ImGuiCol_Text);\n    center -= ImVec2(0.5f, 0.5f);\n    window->DrawList->AddLine(center + ImVec2(+cross_extent, +cross_extent), center + ImVec2(-cross_extent, -cross_extent), cross_col, 1.0f);\n    window->DrawList->AddLine(center + ImVec2(+cross_extent, -cross_extent), center + ImVec2(-cross_extent, +cross_extent), cross_col, 1.0f);\n\n    return pressed;\n}\n\nbool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    ImRect bb(pos, pos + ImVec2(g.FontSize, g.FontSize));\n    bool is_clipped = !ItemAdd(bb, id);\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_None);\n    if (is_clipped)\n        return pressed;\n\n    // Render\n    ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n    ImU32 text_col = GetColorU32(ImGuiCol_Text);\n    if (hovered || held)\n        window->DrawList->AddCircleFilled(bb.GetCenter() + ImVec2(0.0f, -0.5f), g.FontSize * 0.5f + 1.0f, bg_col);\n    RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f);\n\n    // Switch to moving the window after mouse is moved beyond the initial drag threshold\n    if (IsItemActive() && IsMouseDragging(0))\n        StartMouseMovingWindow(window);\n\n    return pressed;\n}\n\nImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis)\n{\n    return window->GetID(axis == ImGuiAxis_X ? \"#SCROLLX\" : \"#SCROLLY\");\n}\n\n// Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set.\nImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis)\n{\n    const ImRect outer_rect = window->Rect();\n    const ImRect inner_rect = window->InnerRect;\n    const float border_size = window->WindowBorderSize;\n    const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar)\n    IM_ASSERT(scrollbar_size > 0.0f);\n    if (axis == ImGuiAxis_X)\n        return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size);\n    else\n        return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size);\n}\n\nvoid ImGui::Scrollbar(ImGuiAxis axis)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    const ImGuiID id = GetWindowScrollbarID(window, axis);\n\n    // Calculate scrollbar bounding box\n    ImRect bb = GetWindowScrollbarRect(window, axis);\n    ImDrawFlags rounding_corners = ImDrawFlags_RoundCornersNone;\n    if (axis == ImGuiAxis_X)\n    {\n        rounding_corners |= ImDrawFlags_RoundCornersBottomLeft;\n        if (!window->ScrollbarY)\n            rounding_corners |= ImDrawFlags_RoundCornersBottomRight;\n    }\n    else\n    {\n        if ((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar))\n            rounding_corners |= ImDrawFlags_RoundCornersTopRight;\n        if (!window->ScrollbarX)\n            rounding_corners |= ImDrawFlags_RoundCornersBottomRight;\n    }\n    float size_avail = window->InnerRect.Max[axis] - window->InnerRect.Min[axis];\n    float size_contents = window->ContentSize[axis] + window->WindowPadding[axis] * 2.0f;\n    ImS64 scroll = (ImS64)window->Scroll[axis];\n    ScrollbarEx(bb, id, axis, &scroll, (ImS64)size_avail, (ImS64)size_contents, rounding_corners);\n    window->Scroll[axis] = (float)scroll;\n}\n\n// Vertical/Horizontal scrollbar\n// The entire piece of code below is rather confusing because:\n// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab)\n// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar\n// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal.\n// Still, the code should probably be made simpler..\nbool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 size_avail_v, ImS64 size_contents_v, ImDrawFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    const float bb_frame_width = bb_frame.GetWidth();\n    const float bb_frame_height = bb_frame.GetHeight();\n    if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f)\n        return false;\n\n    // When we are too small, start hiding and disabling the grab (this reduce visual noise on very small window and facilitate using the window resize grab)\n    float alpha = 1.0f;\n    if ((axis == ImGuiAxis_Y) && bb_frame_height < g.FontSize + g.Style.FramePadding.y * 2.0f)\n        alpha = ImSaturate((bb_frame_height - g.FontSize) / (g.Style.FramePadding.y * 2.0f));\n    if (alpha <= 0.0f)\n        return false;\n\n    const ImGuiStyle& style = g.Style;\n    const bool allow_interaction = (alpha >= 1.0f);\n\n    ImRect bb = bb_frame;\n    bb.Expand(ImVec2(-ImClamp(IM_TRUNC((bb_frame_width - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp(IM_TRUNC((bb_frame_height - 2.0f) * 0.5f), 0.0f, 3.0f)));\n\n    // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar)\n    const float scrollbar_size_v = (axis == ImGuiAxis_X) ? bb.GetWidth() : bb.GetHeight();\n\n    // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount)\n    // But we maintain a minimum size in pixel to allow for the user to still aim inside.\n    IM_ASSERT(ImMax(size_contents_v, size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers.\n    const ImS64 win_size_v = ImMax(ImMax(size_contents_v, size_avail_v), (ImS64)1);\n    const float grab_h_pixels = ImClamp(scrollbar_size_v * ((float)size_avail_v / (float)win_size_v), style.GrabMinSize, scrollbar_size_v);\n    const float grab_h_norm = grab_h_pixels / scrollbar_size_v;\n\n    // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar().\n    bool held = false;\n    bool hovered = false;\n    ItemAdd(bb_frame, id, NULL, ImGuiItemFlags_NoNav);\n    ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus);\n\n    const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_avail_v);\n    float scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max);\n    float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; // Grab position in normalized space\n    if (held && allow_interaction && grab_h_norm < 1.0f)\n    {\n        const float scrollbar_pos_v = bb.Min[axis];\n        const float mouse_pos_v = g.IO.MousePos[axis];\n\n        // Click position in scrollbar normalized space (0.0f->1.0f)\n        const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v);\n        SetHoveredID(id);\n\n        bool seek_absolute = false;\n        if (g.ActiveIdIsJustActivated)\n        {\n            // On initial click calculate the distance between mouse and the center of the grab\n            seek_absolute = (clicked_v_norm < grab_v_norm || clicked_v_norm > grab_v_norm + grab_h_norm);\n            if (seek_absolute)\n                g.ScrollbarClickDeltaToGrabCenter = 0.0f;\n            else\n                g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f;\n        }\n\n        // Apply scroll (p_scroll_v will generally point on one member of window->Scroll)\n        // It is ok to modify Scroll here because we are being called in Begin() after the calculation of ContentSize and before setting up our starting position\n        const float scroll_v_norm = ImSaturate((clicked_v_norm - g.ScrollbarClickDeltaToGrabCenter - grab_h_norm * 0.5f) / (1.0f - grab_h_norm));\n        *p_scroll_v = (ImS64)(scroll_v_norm * scroll_max);\n\n        // Update values for rendering\n        scroll_ratio = ImSaturate((float)*p_scroll_v / (float)scroll_max);\n        grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v;\n\n        // Update distance to grab now that we have seeked and saturated\n        if (seek_absolute)\n            g.ScrollbarClickDeltaToGrabCenter = clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f;\n    }\n\n    // Render\n    const ImU32 bg_col = GetColorU32(ImGuiCol_ScrollbarBg);\n    const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab, alpha);\n    window->DrawList->AddRectFilled(bb_frame.Min, bb_frame.Max, bg_col, window->WindowRounding, flags);\n    ImRect grab_rect;\n    if (axis == ImGuiAxis_X)\n        grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y);\n    else\n        grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels);\n    window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding);\n\n    return held;\n}\n\nvoid ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);\n    if (border_col.w > 0.0f)\n        bb.Max += ImVec2(2, 2);\n    ItemSize(bb);\n    if (!ItemAdd(bb, 0))\n        return;\n\n    if (border_col.w > 0.0f)\n    {\n        window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f);\n        window->DrawList->AddImage(user_texture_id, bb.Min + ImVec2(1, 1), bb.Max - ImVec2(1, 1), uv0, uv1, GetColorU32(tint_col));\n    }\n    else\n    {\n        window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col));\n    }\n}\n\n// ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390)\n// We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API.\nbool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    const ImVec2 padding = g.Style.FramePadding;\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + image_size + padding * 2.0f);\n    ItemSize(bb);\n    if (!ItemAdd(bb, id))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags);\n\n    // Render\n    const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n    RenderNavHighlight(bb, id);\n    RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));\n    if (bg_col.w > 0.0f)\n        window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, GetColorU32(bg_col));\n    window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, GetColorU32(tint_col));\n\n    return pressed;\n}\n\n// Note that ImageButton() adds style.FramePadding*2.0f to provided size. This is in order to facilitate fitting an image in a button.\nbool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& image_size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    return ImageButtonEx(window->GetID(str_id), user_texture_id, image_size, uv0, uv1, bg_col, tint_col);\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n// Legacy API obsoleted in 1.89. Two differences with new ImageButton()\n// - new ImageButton() requires an explicit 'const char* str_id'    Old ImageButton() used opaque imTextureId (created issue with: multiple buttons with same image, transient texture id values, opaque computation of ID)\n// - new ImageButton() always use style.FramePadding                Old ImageButton() had an override argument.\n// If you need to change padding with new ImageButton() you can use PushStyleVar(ImGuiStyleVar_FramePadding, value), consistent with other Button functions.\nbool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    // Default to using texture ID as ID. User can still push string/integer prefixes.\n    PushID((void*)(intptr_t)user_texture_id);\n    const ImGuiID id = window->GetID(\"#image\");\n    PopID();\n\n    if (frame_padding >= 0)\n        PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2((float)frame_padding, (float)frame_padding));\n    bool ret = ImageButtonEx(id, user_texture_id, size, uv0, uv1, bg_col, tint_col);\n    if (frame_padding >= 0)\n        PopStyleVar();\n    return ret;\n}\n#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n\nbool ImGui::Checkbox(const char* label, bool* v)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    const float square_sz = GetFrameHeight();\n    const ImVec2 pos = window->DC.CursorPos;\n    const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, id))\n    {\n        IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));\n        return false;\n    }\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);\n    if (pressed)\n    {\n        *v = !(*v);\n        MarkItemEdited(id);\n    }\n\n    const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));\n    RenderNavHighlight(total_bb, id);\n    RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding);\n    ImU32 check_col = GetColorU32(ImGuiCol_CheckMark);\n    bool mixed_value = (g.LastItemData.InFlags & ImGuiItemFlags_MixedValue) != 0;\n    if (mixed_value)\n    {\n        // Undocumented tristate/mixed/indeterminate checkbox (#2644)\n        // This may seem awkwardly designed because the aim is to make ImGuiItemFlags_MixedValue supported by all widgets (not just checkbox)\n        ImVec2 pad(ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)), ImMax(1.0f, IM_TRUNC(square_sz / 3.6f)));\n        window->DrawList->AddRectFilled(check_bb.Min + pad, check_bb.Max - pad, check_col, style.FrameRounding);\n    }\n    else if (*v)\n    {\n        const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));\n        RenderCheckMark(window->DrawList, check_bb.Min + ImVec2(pad, pad), check_col, square_sz - pad * 2.0f);\n    }\n\n    ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);\n    if (g.LogEnabled)\n        LogRenderedText(&label_pos, mixed_value ? \"[~]\" : *v ? \"[x]\" : \"[ ]\");\n    if (label_size.x > 0.0f)\n        RenderText(label_pos, label);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));\n    return pressed;\n}\n\ntemplate<typename T>\nbool ImGui::CheckboxFlagsT(const char* label, T* flags, T flags_value)\n{\n    bool all_on = (*flags & flags_value) == flags_value;\n    bool any_on = (*flags & flags_value) != 0;\n    bool pressed;\n    if (!all_on && any_on)\n    {\n        ImGuiContext& g = *GImGui;\n        g.NextItemData.ItemFlags |= ImGuiItemFlags_MixedValue;\n        pressed = Checkbox(label, &all_on);\n    }\n    else\n    {\n        pressed = Checkbox(label, &all_on);\n\n    }\n    if (pressed)\n    {\n        if (all_on)\n            *flags |= flags_value;\n        else\n            *flags &= ~flags_value;\n    }\n    return pressed;\n}\n\nbool ImGui::CheckboxFlags(const char* label, int* flags, int flags_value)\n{\n    return CheckboxFlagsT(label, flags, flags_value);\n}\n\nbool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value)\n{\n    return CheckboxFlagsT(label, flags, flags_value);\n}\n\nbool ImGui::CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value)\n{\n    return CheckboxFlagsT(label, flags, flags_value);\n}\n\nbool ImGui::CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value)\n{\n    return CheckboxFlagsT(label, flags, flags_value);\n}\n\nbool ImGui::RadioButton(const char* label, bool active)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    const float square_sz = GetFrameHeight();\n    const ImVec2 pos = window->DC.CursorPos;\n    const ImRect check_bb(pos, pos + ImVec2(square_sz, square_sz));\n    const ImRect total_bb(pos, pos + ImVec2(square_sz + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f));\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, id))\n        return false;\n\n    ImVec2 center = check_bb.GetCenter();\n    center.x = IM_ROUND(center.x);\n    center.y = IM_ROUND(center.y);\n    const float radius = (square_sz - 1.0f) * 0.5f;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(total_bb, id, &hovered, &held);\n    if (pressed)\n        MarkItemEdited(id);\n\n    RenderNavHighlight(total_bb, id);\n    const int num_segment = window->DrawList->_CalcCircleAutoSegmentCount(radius);\n    window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), num_segment);\n    if (active)\n    {\n        const float pad = ImMax(1.0f, IM_TRUNC(square_sz / 6.0f));\n        window->DrawList->AddCircleFilled(center, radius - pad, GetColorU32(ImGuiCol_CheckMark));\n    }\n\n    if (style.FrameBorderSize > 0.0f)\n    {\n        window->DrawList->AddCircle(center + ImVec2(1, 1), radius, GetColorU32(ImGuiCol_BorderShadow), num_segment, style.FrameBorderSize);\n        window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), num_segment, style.FrameBorderSize);\n    }\n\n    ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);\n    if (g.LogEnabled)\n        LogRenderedText(&label_pos, active ? \"(x)\" : \"( )\");\n    if (label_size.x > 0.0f)\n        RenderText(label_pos, label);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n    return pressed;\n}\n\n// FIXME: This would work nicely if it was a public template, e.g. 'template<T> RadioButton(const char* label, T* v, T v_button)', but I'm not sure how we would expose it..\nbool ImGui::RadioButton(const char* label, int* v, int v_button)\n{\n    const bool pressed = RadioButton(label, *v == v_button);\n    if (pressed)\n        *v = v_button;\n    return pressed;\n}\n\n// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size\nvoid ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n\n    ImVec2 pos = window->DC.CursorPos;\n    ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y * 2.0f);\n    ImRect bb(pos, pos + size);\n    ItemSize(size, style.FramePadding.y);\n    if (!ItemAdd(bb, 0))\n        return;\n\n    // Render\n    fraction = ImSaturate(fraction);\n    RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);\n    bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize));\n    const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y);\n    RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding);\n\n    // Default displaying the fraction as percentage string, but user can override it\n    char overlay_buf[32];\n    if (!overlay)\n    {\n        ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), \"%.0f%%\", fraction * 100 + 0.01f);\n        overlay = overlay_buf;\n    }\n\n    ImVec2 overlay_size = CalcTextSize(overlay, NULL);\n    if (overlay_size.x > 0.0f)\n        RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f, 0.5f), &bb);\n}\n\nvoid ImGui::Bullet()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const float line_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), g.FontSize);\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height));\n    ItemSize(bb);\n    if (!ItemAdd(bb, 0))\n    {\n        SameLine(0, style.FramePadding.x * 2);\n        return;\n    }\n\n    // Render and stay on same line\n    ImU32 text_col = GetColorU32(ImGuiCol_Text);\n    RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x + g.FontSize * 0.5f, line_height * 0.5f), text_col);\n    SameLine(0, style.FramePadding.x * 2.0f);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Low-level Layout helpers\n//-------------------------------------------------------------------------\n// - Spacing()\n// - Dummy()\n// - NewLine()\n// - AlignTextToFramePadding()\n// - SeparatorEx() [Internal]\n// - Separator()\n// - SplitterBehavior() [Internal]\n// - ShrinkWidths() [Internal]\n//-------------------------------------------------------------------------\n\nvoid ImGui::Spacing()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n    ItemSize(ImVec2(0, 0));\n}\n\nvoid ImGui::Dummy(const ImVec2& size)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);\n    ItemSize(size);\n    ItemAdd(bb, 0);\n}\n\nvoid ImGui::NewLine()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiLayoutType backup_layout_type = window->DC.LayoutType;\n    window->DC.LayoutType = ImGuiLayoutType_Vertical;\n    window->DC.IsSameLine = false;\n    if (window->DC.CurrLineSize.y > 0.0f)     // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height.\n        ItemSize(ImVec2(0, 0));\n    else\n        ItemSize(ImVec2(0.0f, g.FontSize));\n    window->DC.LayoutType = backup_layout_type;\n}\n\nvoid ImGui::AlignTextToFramePadding()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    window->DC.CurrLineSize.y = ImMax(window->DC.CurrLineSize.y, g.FontSize + g.Style.FramePadding.y * 2);\n    window->DC.CurrLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, g.Style.FramePadding.y);\n}\n\n// Horizontal/vertical separating line\n// FIXME: Surprisingly, this seemingly trivial widget is a victim of many different legacy/tricky layout issues.\n// Note how thickness == 1.0f is handled specifically as not moving CursorPos by 'thickness', but other values are.\nvoid ImGui::SeparatorEx(ImGuiSeparatorFlags flags, float thickness)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(ImIsPowerOfTwo(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)));   // Check that only 1 option is selected\n    IM_ASSERT(thickness > 0.0f);\n\n    if (flags & ImGuiSeparatorFlags_Vertical)\n    {\n        // Vertical separator, for menu bars (use current line height).\n        float y1 = window->DC.CursorPos.y;\n        float y2 = window->DC.CursorPos.y + window->DC.CurrLineSize.y;\n        const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + thickness, y2));\n        ItemSize(ImVec2(thickness, 0.0f));\n        if (!ItemAdd(bb, 0))\n            return;\n\n        // Draw\n        window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator));\n        if (g.LogEnabled)\n            LogText(\" |\");\n    }\n    else if (flags & ImGuiSeparatorFlags_Horizontal)\n    {\n        // Horizontal Separator\n        float x1 = window->DC.CursorPos.x;\n        float x2 = window->WorkRect.Max.x;\n\n        // Preserve legacy behavior inside Columns()\n        // Before Tables API happened, we relied on Separator() to span all columns of a Columns() set.\n        // We currently don't need to provide the same feature for tables because tables naturally have border features.\n        ImGuiOldColumns* columns = (flags & ImGuiSeparatorFlags_SpanAllColumns) ? window->DC.CurrentColumns : NULL;\n        if (columns)\n        {\n            x1 = window->Pos.x + window->DC.Indent.x; // Used to be Pos.x before 2023/10/03\n            x2 = window->Pos.x + window->Size.x;\n            PushColumnsBackground();\n        }\n\n        // We don't provide our width to the layout so that it doesn't get feed back into AutoFit\n        // FIXME: This prevents ->CursorMaxPos based bounding box evaluation from working (e.g. TableEndCell)\n        const float thickness_for_layout = (thickness == 1.0f) ? 0.0f : thickness; // FIXME: See 1.70/1.71 Separator() change: makes legacy 1-px separator not affect layout yet. Should change.\n        const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y + thickness));\n        ItemSize(ImVec2(0.0f, thickness_for_layout));\n\n        if (ItemAdd(bb, 0))\n        {\n            // Draw\n            window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator));\n            if (g.LogEnabled)\n                LogRenderedText(&bb.Min, \"--------------------------------\\n\");\n\n        }\n        if (columns)\n        {\n            PopColumnsBackground();\n            columns->LineMinY = window->DC.CursorPos.y;\n        }\n    }\n}\n\nvoid ImGui::Separator()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    // Those flags should eventually be configurable by the user\n    // FIXME: We cannot g.Style.SeparatorTextBorderSize for thickness as it relates to SeparatorText() which is a decorated separator, not defaulting to 1.0f.\n    ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal;\n\n    // Only applies to legacy Columns() api as they relied on Separator() a lot.\n    if (window->DC.CurrentColumns)\n        flags |= ImGuiSeparatorFlags_SpanAllColumns;\n\n    SeparatorEx(flags, 1.0f);\n}\n\nvoid ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end, float extra_w)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiStyle& style = g.Style;\n\n    const ImVec2 label_size = CalcTextSize(label, label_end, false);\n    const ImVec2 pos = window->DC.CursorPos;\n    const ImVec2 padding = style.SeparatorTextPadding;\n\n    const float separator_thickness = style.SeparatorTextBorderSize;\n    const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness));\n    const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y));\n    const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f));\n    ItemSize(min_size, text_baseline_y);\n    if (!ItemAdd(bb, id))\n        return;\n\n    const float sep1_x1 = pos.x;\n    const float sep2_x2 = bb.Max.x;\n    const float seps_y = ImTrunc((bb.Min.y + bb.Max.y) * 0.5f + 0.99999f);\n\n    const float label_avail_w = ImMax(0.0f, sep2_x2 - sep1_x1 - padding.x * 2.0f);\n    const ImVec2 label_pos(pos.x + padding.x + ImMax(0.0f, (label_avail_w - label_size.x - extra_w) * style.SeparatorTextAlign.x), pos.y + text_baseline_y); // FIXME-ALIGN\n\n    // This allows using SameLine() to position something in the 'extra_w'\n    window->DC.CursorPosPrevLine.x = label_pos.x + label_size.x;\n\n    const ImU32 separator_col = GetColorU32(ImGuiCol_Separator);\n    if (label_size.x > 0.0f)\n    {\n        const float sep1_x2 = label_pos.x - style.ItemSpacing.x;\n        const float sep2_x1 = label_pos.x + label_size.x + extra_w + style.ItemSpacing.x;\n        if (sep1_x2 > sep1_x1 && separator_thickness > 0.0f)\n            window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep1_x2, seps_y), separator_col, separator_thickness);\n        if (sep2_x2 > sep2_x1 && separator_thickness > 0.0f)\n            window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);\n        if (g.LogEnabled)\n            LogSetNextTextDecoration(\"---\", NULL);\n        RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, bb.Max.x, label, label_end, &label_size);\n    }\n    else\n    {\n        if (g.LogEnabled)\n            LogText(\"---\");\n        if (separator_thickness > 0.0f)\n            window->DrawList->AddLine(ImVec2(sep1_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);\n    }\n}\n\nvoid ImGui::SeparatorText(const char* label)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n\n    // The SeparatorText() vs SeparatorTextEx() distinction is designed to be considerate that we may want:\n    // - allow separator-text to be draggable items (would require a stable ID + a noticeable highlight)\n    // - this high-level entry point to allow formatting? (which in turns may require ID separate from formatted string)\n    // - because of this we probably can't turn 'const char* label' into 'const char* fmt, ...'\n    // Otherwise, we can decide that users wanting to drag this would layout a dedicated drag-item,\n    // and then we can turn this into a format function.\n    SeparatorTextEx(0, label, FindRenderedTextEnd(label), 0.0f);\n}\n\n// Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise.\nbool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay, ImU32 bg_col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav))\n        return false;\n\n    // FIXME: AFAIK the only leftover reason for passing ImGuiButtonFlags_AllowOverlap here is\n    // to allow caller of SplitterBehavior() to call SetItemAllowOverlap() after the item.\n    // Nowadays we would instead want to use SetNextItemAllowOverlap() before the item.\n    ImGuiButtonFlags button_flags = ImGuiButtonFlags_FlattenChildren;\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n    button_flags |= ImGuiButtonFlags_AllowOverlap;\n#endif\n\n    bool hovered, held;\n    ImRect bb_interact = bb;\n    bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f));\n    ButtonBehavior(bb_interact, id, &hovered, &held, button_flags);\n    if (hovered)\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; // for IsItemHovered(), because bb_interact is larger than bb\n\n    if (held || (hovered && g.HoveredIdPreviousFrame == id && g.HoveredIdTimer >= hover_visibility_delay))\n        SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);\n\n    ImRect bb_render = bb;\n    if (held)\n    {\n        float mouse_delta = (g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min)[axis];\n\n        // Minimum pane size\n        float size_1_maximum_delta = ImMax(0.0f, *size1 - min_size1);\n        float size_2_maximum_delta = ImMax(0.0f, *size2 - min_size2);\n        if (mouse_delta < -size_1_maximum_delta)\n            mouse_delta = -size_1_maximum_delta;\n        if (mouse_delta > size_2_maximum_delta)\n            mouse_delta = size_2_maximum_delta;\n\n        // Apply resize\n        if (mouse_delta != 0.0f)\n        {\n            *size1 = ImMax(*size1 + mouse_delta, min_size1);\n            *size2 = ImMax(*size2 - mouse_delta, min_size2);\n            bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta));\n            MarkItemEdited(id);\n        }\n    }\n\n    // Render at new position\n    if (bg_col & IM_COL32_A_MASK)\n        window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0f);\n    const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator);\n    window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0f);\n\n    return held;\n}\n\nstatic int IMGUI_CDECL ShrinkWidthItemComparer(const void* lhs, const void* rhs)\n{\n    const ImGuiShrinkWidthItem* a = (const ImGuiShrinkWidthItem*)lhs;\n    const ImGuiShrinkWidthItem* b = (const ImGuiShrinkWidthItem*)rhs;\n    if (int d = (int)(b->Width - a->Width))\n        return d;\n    return (b->Index - a->Index);\n}\n\n// Shrink excess width from a set of item, by removing width from the larger items first.\n// Set items Width to -1.0f to disable shrinking this item.\nvoid ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess)\n{\n    if (count == 1)\n    {\n        if (items[0].Width >= 0.0f)\n            items[0].Width = ImMax(items[0].Width - width_excess, 1.0f);\n        return;\n    }\n    ImQsort(items, (size_t)count, sizeof(ImGuiShrinkWidthItem), ShrinkWidthItemComparer);\n    int count_same_width = 1;\n    while (width_excess > 0.0f && count_same_width < count)\n    {\n        while (count_same_width < count && items[0].Width <= items[count_same_width].Width)\n            count_same_width++;\n        float max_width_to_remove_per_item = (count_same_width < count && items[count_same_width].Width >= 0.0f) ? (items[0].Width - items[count_same_width].Width) : (items[0].Width - 1.0f);\n        if (max_width_to_remove_per_item <= 0.0f)\n            break;\n        float width_to_remove_per_item = ImMin(width_excess / count_same_width, max_width_to_remove_per_item);\n        for (int item_n = 0; item_n < count_same_width; item_n++)\n            items[item_n].Width -= width_to_remove_per_item;\n        width_excess -= width_to_remove_per_item * count_same_width;\n    }\n\n    // Round width and redistribute remainder\n    // Ensure that e.g. the right-most tab of a shrunk tab-bar always reaches exactly at the same distance from the right-most edge of the tab bar separator.\n    width_excess = 0.0f;\n    for (int n = 0; n < count; n++)\n    {\n        float width_rounded = ImTrunc(items[n].Width);\n        width_excess += items[n].Width - width_rounded;\n        items[n].Width = width_rounded;\n    }\n    while (width_excess > 0.0f)\n        for (int n = 0; n < count && width_excess > 0.0f; n++)\n        {\n            float width_to_add = ImMin(items[n].InitialWidth - items[n].Width, 1.0f);\n            items[n].Width += width_to_add;\n            width_excess -= width_to_add;\n        }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: ComboBox\n//-------------------------------------------------------------------------\n// - CalcMaxPopupHeightFromItemCount() [Internal]\n// - BeginCombo()\n// - BeginComboPopup() [Internal]\n// - EndCombo()\n// - BeginComboPreview() [Internal]\n// - EndComboPreview() [Internal]\n// - Combo()\n//-------------------------------------------------------------------------\n\nstatic float CalcMaxPopupHeightFromItemCount(int items_count)\n{\n    ImGuiContext& g = *GImGui;\n    if (items_count <= 0)\n        return FLT_MAX;\n    return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2);\n}\n\nbool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n\n    ImGuiNextWindowDataFlags backup_next_window_data_flags = g.NextWindowData.Flags;\n    g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n    if (window->SkipItems)\n        return false;\n\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together\n    if (flags & ImGuiComboFlags_WidthFitPreview)\n        IM_ASSERT((flags & (ImGuiComboFlags_NoPreview | ImGuiComboFlags_CustomPreview)) == 0);\n\n    const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight();\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const float preview_width = ((flags & ImGuiComboFlags_WidthFitPreview) && (preview_value != NULL)) ? CalcTextSize(preview_value, NULL, true).x : 0.0f;\n    const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : ((flags & ImGuiComboFlags_WidthFitPreview) ? (arrow_size + preview_width + style.FramePadding.x * 2.0f) : CalcItemWidth());\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));\n    const ImRect total_bb(bb.Min, bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, id, &bb))\n        return false;\n\n    // Open on click\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held);\n    const ImGuiID popup_id = ImHashStr(\"##ComboPopup\", 0, id);\n    bool popup_open = IsPopupOpen(popup_id, ImGuiPopupFlags_None);\n    if (pressed && !popup_open)\n    {\n        OpenPopupEx(popup_id, ImGuiPopupFlags_None);\n        popup_open = true;\n    }\n\n    // Render shape\n    const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);\n    const float value_x2 = ImMax(bb.Min.x, bb.Max.x - arrow_size);\n    RenderNavHighlight(bb, id);\n    if (!(flags & ImGuiComboFlags_NoPreview))\n        window->DrawList->AddRectFilled(bb.Min, ImVec2(value_x2, bb.Max.y), frame_col, style.FrameRounding, (flags & ImGuiComboFlags_NoArrowButton) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersLeft);\n    if (!(flags & ImGuiComboFlags_NoArrowButton))\n    {\n        ImU32 bg_col = GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button);\n        ImU32 text_col = GetColorU32(ImGuiCol_Text);\n        window->DrawList->AddRectFilled(ImVec2(value_x2, bb.Min.y), bb.Max, bg_col, style.FrameRounding, (w <= arrow_size) ? ImDrawFlags_RoundCornersAll : ImDrawFlags_RoundCornersRight);\n        if (value_x2 + arrow_size - style.FramePadding.x <= bb.Max.x)\n            RenderArrow(window->DrawList, ImVec2(value_x2 + style.FramePadding.y, bb.Min.y + style.FramePadding.y), text_col, ImGuiDir_Down, 1.0f);\n    }\n    RenderFrameBorder(bb.Min, bb.Max, style.FrameRounding);\n\n    // Custom preview\n    if (flags & ImGuiComboFlags_CustomPreview)\n    {\n        g.ComboPreviewData.PreviewRect = ImRect(bb.Min.x, bb.Min.y, value_x2, bb.Max.y);\n        IM_ASSERT(preview_value == NULL || preview_value[0] == 0);\n        preview_value = NULL;\n    }\n\n    // Render preview and label\n    if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview))\n    {\n        if (g.LogEnabled)\n            LogSetNextTextDecoration(\"{\", \"}\");\n        RenderTextClipped(bb.Min + style.FramePadding, ImVec2(value_x2, bb.Max.y), preview_value, NULL, NULL);\n    }\n    if (label_size.x > 0)\n        RenderText(ImVec2(bb.Max.x + style.ItemInnerSpacing.x, bb.Min.y + style.FramePadding.y), label);\n\n    if (!popup_open)\n        return false;\n\n    g.NextWindowData.Flags = backup_next_window_data_flags;\n    return BeginComboPopup(popup_id, bb, flags);\n}\n\nbool ImGui::BeginComboPopup(ImGuiID popup_id, const ImRect& bb, ImGuiComboFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if (!IsPopupOpen(popup_id, ImGuiPopupFlags_None))\n    {\n        g.NextWindowData.ClearFlags();\n        return false;\n    }\n\n    // Set popup size\n    float w = bb.GetWidth();\n    if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint)\n    {\n        g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w);\n    }\n    else\n    {\n        if ((flags & ImGuiComboFlags_HeightMask_) == 0)\n            flags |= ImGuiComboFlags_HeightRegular;\n        IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one\n        int popup_max_height_in_items = -1;\n        if (flags & ImGuiComboFlags_HeightRegular)     popup_max_height_in_items = 8;\n        else if (flags & ImGuiComboFlags_HeightSmall)  popup_max_height_in_items = 4;\n        else if (flags & ImGuiComboFlags_HeightLarge)  popup_max_height_in_items = 20;\n        ImVec2 constraint_min(0.0f, 0.0f), constraint_max(FLT_MAX, FLT_MAX);\n        if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.x <= 0.0f) // Don't apply constraints if user specified a size\n            constraint_min.x = w;\n        if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) == 0 || g.NextWindowData.SizeVal.y <= 0.0f)\n            constraint_max.y = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items);\n        SetNextWindowSizeConstraints(constraint_min, constraint_max);\n    }\n\n    // This is essentially a specialized version of BeginPopupEx()\n    char name[16];\n    ImFormatString(name, IM_ARRAYSIZE(name), \"##Combo_%02d\", g.BeginPopupStack.Size); // Recycle windows based on depth\n\n    // Set position given a custom constraint (peak into expected window size so we can position it)\n    // FIXME: This might be easier to express with an hypothetical SetNextWindowPosConstraints() function?\n    // FIXME: This might be moved to Begin() or at least around the same spot where Tooltips and other Popups are calling FindBestWindowPosForPopupEx()?\n    if (ImGuiWindow* popup_window = FindWindowByName(name))\n        if (popup_window->WasActive)\n        {\n            // Always override 'AutoPosLastDirection' to not leave a chance for a past value to affect us.\n            ImVec2 size_expected = CalcWindowNextAutoFitSize(popup_window);\n            popup_window->AutoPosLastDirection = (flags & ImGuiComboFlags_PopupAlignLeft) ? ImGuiDir_Left : ImGuiDir_Down; // Left = \"Below, Toward Left\", Down = \"Below, Toward Right (default)\"\n            ImRect r_outer = GetPopupAllowedExtentRect(popup_window);\n            ImVec2 pos = FindBestWindowPosForPopupEx(bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, bb, ImGuiPopupPositionPolicy_ComboBox);\n            SetNextWindowPos(pos);\n        }\n\n    // We don't use BeginPopupEx() solely because we have a custom name string, which we could make an argument to BeginPopupEx()\n    ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove;\n    PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(g.Style.FramePadding.x, g.Style.WindowPadding.y)); // Horizontally align ourselves with the framed text\n    bool ret = Begin(name, NULL, window_flags);\n    PopStyleVar();\n    if (!ret)\n    {\n        EndPopup();\n        IM_ASSERT(0);   // This should never happen as we tested for IsPopupOpen() above\n        return false;\n    }\n    return true;\n}\n\nvoid ImGui::EndCombo()\n{\n    EndPopup();\n}\n\n// Call directly after the BeginCombo/EndCombo block. The preview is designed to only host non-interactive elements\n// (Experimental, see GitHub issues: #1658, #4168)\nbool ImGui::BeginComboPreview()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiComboPreviewData* preview_data = &g.ComboPreviewData;\n\n    if (window->SkipItems || !(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible))\n        return false;\n    IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag?\n    if (!window->ClipRect.Overlaps(preview_data->PreviewRect)) // Narrower test (optional)\n        return false;\n\n    // FIXME: This could be contained in a PushWorkRect() api\n    preview_data->BackupCursorPos = window->DC.CursorPos;\n    preview_data->BackupCursorMaxPos = window->DC.CursorMaxPos;\n    preview_data->BackupCursorPosPrevLine = window->DC.CursorPosPrevLine;\n    preview_data->BackupPrevLineTextBaseOffset = window->DC.PrevLineTextBaseOffset;\n    preview_data->BackupLayout = window->DC.LayoutType;\n    window->DC.CursorPos = preview_data->PreviewRect.Min + g.Style.FramePadding;\n    window->DC.CursorMaxPos = window->DC.CursorPos;\n    window->DC.LayoutType = ImGuiLayoutType_Horizontal;\n    window->DC.IsSameLine = false;\n    PushClipRect(preview_data->PreviewRect.Min, preview_data->PreviewRect.Max, true);\n\n    return true;\n}\n\nvoid ImGui::EndComboPreview()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiComboPreviewData* preview_data = &g.ComboPreviewData;\n\n    // FIXME: Using CursorMaxPos approximation instead of correct AABB which we will store in ImDrawCmd in the future\n    ImDrawList* draw_list = window->DrawList;\n    if (window->DC.CursorMaxPos.x < preview_data->PreviewRect.Max.x && window->DC.CursorMaxPos.y < preview_data->PreviewRect.Max.y)\n        if (draw_list->CmdBuffer.Size > 1) // Unlikely case that the PushClipRect() didn't create a command\n        {\n            draw_list->_CmdHeader.ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ClipRect = draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 2].ClipRect;\n            draw_list->_TryMergeDrawCmds();\n        }\n    PopClipRect();\n    window->DC.CursorPos = preview_data->BackupCursorPos;\n    window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, preview_data->BackupCursorMaxPos);\n    window->DC.CursorPosPrevLine = preview_data->BackupCursorPosPrevLine;\n    window->DC.PrevLineTextBaseOffset = preview_data->BackupPrevLineTextBaseOffset;\n    window->DC.LayoutType = preview_data->BackupLayout;\n    window->DC.IsSameLine = false;\n    preview_data->PreviewRect = ImRect();\n}\n\n// Getter for the old Combo() API: const char*[]\nstatic const char* Items_ArrayGetter(void* data, int idx)\n{\n    const char* const* items = (const char* const*)data;\n    return items[idx];\n}\n\n// Getter for the old Combo() API: \"item1\\0item2\\0item3\\0\"\nstatic const char* Items_SingleStringGetter(void* data, int idx)\n{\n    const char* items_separated_by_zeros = (const char*)data;\n    int items_count = 0;\n    const char* p = items_separated_by_zeros;\n    while (*p)\n    {\n        if (idx == items_count)\n            break;\n        p += strlen(p) + 1;\n        items_count++;\n    }\n    return *p ? p : NULL;\n}\n\n// Old API, prefer using BeginCombo() nowadays if you can.\nbool ImGui::Combo(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int popup_max_height_in_items)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Call the getter to obtain the preview string which is a parameter to BeginCombo()\n    const char* preview_value = NULL;\n    if (*current_item >= 0 && *current_item < items_count)\n        preview_value = getter(user_data, *current_item);\n\n    // The old Combo() API exposed \"popup_max_height_in_items\". The new more general BeginCombo() API doesn't have/need it, but we emulate it here.\n    if (popup_max_height_in_items != -1 && !(g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSizeConstraint))\n        SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items)));\n\n    if (!BeginCombo(label, preview_value, ImGuiComboFlags_None))\n        return false;\n\n    // Display items\n    // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed)\n    bool value_changed = false;\n    for (int i = 0; i < items_count; i++)\n    {\n        const char* item_text = getter(user_data, i);\n        if (item_text == NULL)\n            item_text = \"*Unknown item*\";\n\n        PushID(i);\n        const bool item_selected = (i == *current_item);\n        if (Selectable(item_text, item_selected) && *current_item != i)\n        {\n            value_changed = true;\n            *current_item = i;\n        }\n        if (item_selected)\n            SetItemDefaultFocus();\n        PopID();\n    }\n\n    EndCombo();\n\n    if (value_changed)\n        MarkItemEdited(g.LastItemData.ID);\n\n    return value_changed;\n}\n\n// Combo box helper allowing to pass an array of strings.\nbool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items)\n{\n    const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items);\n    return value_changed;\n}\n\n// Combo box helper allowing to pass all items in a single string literal holding multiple zero-terminated items \"item1\\0item2\\0\"\nbool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items)\n{\n    int items_count = 0;\n    const char* p = items_separated_by_zeros;       // FIXME-OPT: Avoid computing this, or at least only when combo is open\n    while (*p)\n    {\n        p += strlen(p) + 1;\n        items_count++;\n    }\n    bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items);\n    return value_changed;\n}\n\n#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS\n\nstruct ImGuiGetNameFromIndexOldToNewCallbackData { void* UserData; bool (*OldCallback)(void*, int, const char**); };\nstatic const char* ImGuiGetNameFromIndexOldToNewCallback(void* user_data, int idx)\n{\n    ImGuiGetNameFromIndexOldToNewCallbackData* data = (ImGuiGetNameFromIndexOldToNewCallbackData*)user_data;\n    const char* s = NULL;\n    data->OldCallback(data->UserData, idx, &s);\n    return s;\n}\n\nbool ImGui::ListBox(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int height_in_items)\n{\n    ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter };\n    return ListBox(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, height_in_items);\n}\nbool ImGui::Combo(const char* label, int* current_item, bool (*old_getter)(void*, int, const char**), void* user_data, int items_count, int popup_max_height_in_items)\n{\n    ImGuiGetNameFromIndexOldToNewCallbackData old_to_new_data = { user_data, old_getter };\n    return Combo(label, current_item, ImGuiGetNameFromIndexOldToNewCallback, &old_to_new_data, items_count, popup_max_height_in_items);\n}\n\n#endif\n\n//-------------------------------------------------------------------------\n// [SECTION] Data Type and Data Formatting Helpers [Internal]\n//-------------------------------------------------------------------------\n// - DataTypeGetInfo()\n// - DataTypeFormatString()\n// - DataTypeApplyOp()\n// - DataTypeApplyOpFromText()\n// - DataTypeCompare()\n// - DataTypeClamp()\n// - GetMinimumStepAtDecimalPrecision\n// - RoundScalarWithFormat<>()\n//-------------------------------------------------------------------------\n\nstatic const ImGuiDataTypeInfo GDataTypeInfo[] =\n{\n    { sizeof(char),             \"S8\",   \"%d\",   \"%d\"    },  // ImGuiDataType_S8\n    { sizeof(unsigned char),    \"U8\",   \"%u\",   \"%u\"    },\n    { sizeof(short),            \"S16\",  \"%d\",   \"%d\"    },  // ImGuiDataType_S16\n    { sizeof(unsigned short),   \"U16\",  \"%u\",   \"%u\"    },\n    { sizeof(int),              \"S32\",  \"%d\",   \"%d\"    },  // ImGuiDataType_S32\n    { sizeof(unsigned int),     \"U32\",  \"%u\",   \"%u\"    },\n#ifdef _MSC_VER\n    { sizeof(ImS64),            \"S64\",  \"%I64d\",\"%I64d\" },  // ImGuiDataType_S64\n    { sizeof(ImU64),            \"U64\",  \"%I64u\",\"%I64u\" },\n#else\n    { sizeof(ImS64),            \"S64\",  \"%lld\", \"%lld\"  },  // ImGuiDataType_S64\n    { sizeof(ImU64),            \"U64\",  \"%llu\", \"%llu\"  },\n#endif\n    { sizeof(float),            \"float\", \"%.3f\",\"%f\"    },  // ImGuiDataType_Float (float are promoted to double in va_arg)\n    { sizeof(double),           \"double\",\"%f\",  \"%lf\"   },  // ImGuiDataType_Double\n};\nIM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT);\n\nconst ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type)\n{\n    IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);\n    return &GDataTypeInfo[data_type];\n}\n\nint ImGui::DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* p_data, const char* format)\n{\n    // Signedness doesn't matter when pushing integer arguments\n    if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32)\n        return ImFormatString(buf, buf_size, format, *(const ImU32*)p_data);\n    if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64)\n        return ImFormatString(buf, buf_size, format, *(const ImU64*)p_data);\n    if (data_type == ImGuiDataType_Float)\n        return ImFormatString(buf, buf_size, format, *(const float*)p_data);\n    if (data_type == ImGuiDataType_Double)\n        return ImFormatString(buf, buf_size, format, *(const double*)p_data);\n    if (data_type == ImGuiDataType_S8)\n        return ImFormatString(buf, buf_size, format, *(const ImS8*)p_data);\n    if (data_type == ImGuiDataType_U8)\n        return ImFormatString(buf, buf_size, format, *(const ImU8*)p_data);\n    if (data_type == ImGuiDataType_S16)\n        return ImFormatString(buf, buf_size, format, *(const ImS16*)p_data);\n    if (data_type == ImGuiDataType_U16)\n        return ImFormatString(buf, buf_size, format, *(const ImU16*)p_data);\n    IM_ASSERT(0);\n    return 0;\n}\n\nvoid ImGui::DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, const void* arg1, const void* arg2)\n{\n    IM_ASSERT(op == '+' || op == '-');\n    switch (data_type)\n    {\n        case ImGuiDataType_S8:\n            if (op == '+') { *(ImS8*)output  = ImAddClampOverflow(*(const ImS8*)arg1,  *(const ImS8*)arg2,  IM_S8_MIN,  IM_S8_MAX); }\n            if (op == '-') { *(ImS8*)output  = ImSubClampOverflow(*(const ImS8*)arg1,  *(const ImS8*)arg2,  IM_S8_MIN,  IM_S8_MAX); }\n            return;\n        case ImGuiDataType_U8:\n            if (op == '+') { *(ImU8*)output  = ImAddClampOverflow(*(const ImU8*)arg1,  *(const ImU8*)arg2,  IM_U8_MIN,  IM_U8_MAX); }\n            if (op == '-') { *(ImU8*)output  = ImSubClampOverflow(*(const ImU8*)arg1,  *(const ImU8*)arg2,  IM_U8_MIN,  IM_U8_MAX); }\n            return;\n        case ImGuiDataType_S16:\n            if (op == '+') { *(ImS16*)output = ImAddClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); }\n            if (op == '-') { *(ImS16*)output = ImSubClampOverflow(*(const ImS16*)arg1, *(const ImS16*)arg2, IM_S16_MIN, IM_S16_MAX); }\n            return;\n        case ImGuiDataType_U16:\n            if (op == '+') { *(ImU16*)output = ImAddClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); }\n            if (op == '-') { *(ImU16*)output = ImSubClampOverflow(*(const ImU16*)arg1, *(const ImU16*)arg2, IM_U16_MIN, IM_U16_MAX); }\n            return;\n        case ImGuiDataType_S32:\n            if (op == '+') { *(ImS32*)output = ImAddClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); }\n            if (op == '-') { *(ImS32*)output = ImSubClampOverflow(*(const ImS32*)arg1, *(const ImS32*)arg2, IM_S32_MIN, IM_S32_MAX); }\n            return;\n        case ImGuiDataType_U32:\n            if (op == '+') { *(ImU32*)output = ImAddClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); }\n            if (op == '-') { *(ImU32*)output = ImSubClampOverflow(*(const ImU32*)arg1, *(const ImU32*)arg2, IM_U32_MIN, IM_U32_MAX); }\n            return;\n        case ImGuiDataType_S64:\n            if (op == '+') { *(ImS64*)output = ImAddClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); }\n            if (op == '-') { *(ImS64*)output = ImSubClampOverflow(*(const ImS64*)arg1, *(const ImS64*)arg2, IM_S64_MIN, IM_S64_MAX); }\n            return;\n        case ImGuiDataType_U64:\n            if (op == '+') { *(ImU64*)output = ImAddClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); }\n            if (op == '-') { *(ImU64*)output = ImSubClampOverflow(*(const ImU64*)arg1, *(const ImU64*)arg2, IM_U64_MIN, IM_U64_MAX); }\n            return;\n        case ImGuiDataType_Float:\n            if (op == '+') { *(float*)output = *(const float*)arg1 + *(const float*)arg2; }\n            if (op == '-') { *(float*)output = *(const float*)arg1 - *(const float*)arg2; }\n            return;\n        case ImGuiDataType_Double:\n            if (op == '+') { *(double*)output = *(const double*)arg1 + *(const double*)arg2; }\n            if (op == '-') { *(double*)output = *(const double*)arg1 - *(const double*)arg2; }\n            return;\n        case ImGuiDataType_COUNT: break;\n    }\n    IM_ASSERT(0);\n}\n\n// User can input math operators (e.g. +100) to edit a numerical values.\n// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess..\nbool ImGui::DataTypeApplyFromText(const char* buf, ImGuiDataType data_type, void* p_data, const char* format)\n{\n    while (ImCharIsBlankA(*buf))\n        buf++;\n    if (!buf[0])\n        return false;\n\n    // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all.\n    const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type);\n    ImGuiDataTypeTempStorage data_backup;\n    memcpy(&data_backup, p_data, type_info->Size);\n\n    // Sanitize format\n    // - For float/double we have to ignore format with precision (e.g. \"%.2f\") because sscanf doesn't take them in, so force them into %f and %lf\n    // - In theory could treat empty format as using default, but this would only cover rare/bizarre case of using InputScalar() + integer + format string without %.\n    char format_sanitized[32];\n    if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)\n        format = type_info->ScanFmt;\n    else\n        format = ImParseFormatSanitizeForScanning(format, format_sanitized, IM_ARRAYSIZE(format_sanitized));\n\n    // Small types need a 32-bit buffer to receive the result from scanf()\n    int v32 = 0;\n    if (sscanf(buf, format, type_info->Size >= 4 ? p_data : &v32) < 1)\n        return false;\n    if (type_info->Size < 4)\n    {\n        if (data_type == ImGuiDataType_S8)\n            *(ImS8*)p_data = (ImS8)ImClamp(v32, (int)IM_S8_MIN, (int)IM_S8_MAX);\n        else if (data_type == ImGuiDataType_U8)\n            *(ImU8*)p_data = (ImU8)ImClamp(v32, (int)IM_U8_MIN, (int)IM_U8_MAX);\n        else if (data_type == ImGuiDataType_S16)\n            *(ImS16*)p_data = (ImS16)ImClamp(v32, (int)IM_S16_MIN, (int)IM_S16_MAX);\n        else if (data_type == ImGuiDataType_U16)\n            *(ImU16*)p_data = (ImU16)ImClamp(v32, (int)IM_U16_MIN, (int)IM_U16_MAX);\n        else\n            IM_ASSERT(0);\n    }\n\n    return memcmp(&data_backup, p_data, type_info->Size) != 0;\n}\n\ntemplate<typename T>\nstatic int DataTypeCompareT(const T* lhs, const T* rhs)\n{\n    if (*lhs < *rhs) return -1;\n    if (*lhs > *rhs) return +1;\n    return 0;\n}\n\nint ImGui::DataTypeCompare(ImGuiDataType data_type, const void* arg_1, const void* arg_2)\n{\n    switch (data_type)\n    {\n    case ImGuiDataType_S8:     return DataTypeCompareT<ImS8  >((const ImS8*  )arg_1, (const ImS8*  )arg_2);\n    case ImGuiDataType_U8:     return DataTypeCompareT<ImU8  >((const ImU8*  )arg_1, (const ImU8*  )arg_2);\n    case ImGuiDataType_S16:    return DataTypeCompareT<ImS16 >((const ImS16* )arg_1, (const ImS16* )arg_2);\n    case ImGuiDataType_U16:    return DataTypeCompareT<ImU16 >((const ImU16* )arg_1, (const ImU16* )arg_2);\n    case ImGuiDataType_S32:    return DataTypeCompareT<ImS32 >((const ImS32* )arg_1, (const ImS32* )arg_2);\n    case ImGuiDataType_U32:    return DataTypeCompareT<ImU32 >((const ImU32* )arg_1, (const ImU32* )arg_2);\n    case ImGuiDataType_S64:    return DataTypeCompareT<ImS64 >((const ImS64* )arg_1, (const ImS64* )arg_2);\n    case ImGuiDataType_U64:    return DataTypeCompareT<ImU64 >((const ImU64* )arg_1, (const ImU64* )arg_2);\n    case ImGuiDataType_Float:  return DataTypeCompareT<float >((const float* )arg_1, (const float* )arg_2);\n    case ImGuiDataType_Double: return DataTypeCompareT<double>((const double*)arg_1, (const double*)arg_2);\n    case ImGuiDataType_COUNT:  break;\n    }\n    IM_ASSERT(0);\n    return 0;\n}\n\ntemplate<typename T>\nstatic bool DataTypeClampT(T* v, const T* v_min, const T* v_max)\n{\n    // Clamp, both sides are optional, return true if modified\n    if (v_min && *v < *v_min) { *v = *v_min; return true; }\n    if (v_max && *v > *v_max) { *v = *v_max; return true; }\n    return false;\n}\n\nbool ImGui::DataTypeClamp(ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max)\n{\n    switch (data_type)\n    {\n    case ImGuiDataType_S8:     return DataTypeClampT<ImS8  >((ImS8*  )p_data, (const ImS8*  )p_min, (const ImS8*  )p_max);\n    case ImGuiDataType_U8:     return DataTypeClampT<ImU8  >((ImU8*  )p_data, (const ImU8*  )p_min, (const ImU8*  )p_max);\n    case ImGuiDataType_S16:    return DataTypeClampT<ImS16 >((ImS16* )p_data, (const ImS16* )p_min, (const ImS16* )p_max);\n    case ImGuiDataType_U16:    return DataTypeClampT<ImU16 >((ImU16* )p_data, (const ImU16* )p_min, (const ImU16* )p_max);\n    case ImGuiDataType_S32:    return DataTypeClampT<ImS32 >((ImS32* )p_data, (const ImS32* )p_min, (const ImS32* )p_max);\n    case ImGuiDataType_U32:    return DataTypeClampT<ImU32 >((ImU32* )p_data, (const ImU32* )p_min, (const ImU32* )p_max);\n    case ImGuiDataType_S64:    return DataTypeClampT<ImS64 >((ImS64* )p_data, (const ImS64* )p_min, (const ImS64* )p_max);\n    case ImGuiDataType_U64:    return DataTypeClampT<ImU64 >((ImU64* )p_data, (const ImU64* )p_min, (const ImU64* )p_max);\n    case ImGuiDataType_Float:  return DataTypeClampT<float >((float* )p_data, (const float* )p_min, (const float* )p_max);\n    case ImGuiDataType_Double: return DataTypeClampT<double>((double*)p_data, (const double*)p_min, (const double*)p_max);\n    case ImGuiDataType_COUNT:  break;\n    }\n    IM_ASSERT(0);\n    return false;\n}\n\nstatic float GetMinimumStepAtDecimalPrecision(int decimal_precision)\n{\n    static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f };\n    if (decimal_precision < 0)\n        return FLT_MIN;\n    return (decimal_precision < IM_ARRAYSIZE(min_steps)) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision);\n}\n\ntemplate<typename TYPE>\nTYPE ImGui::RoundScalarWithFormatT(const char* format, ImGuiDataType data_type, TYPE v)\n{\n    IM_UNUSED(data_type);\n    IM_ASSERT(data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double);\n    const char* fmt_start = ImParseFormatFindStart(format);\n    if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string\n        return v;\n\n    // Sanitize format\n    char fmt_sanitized[32];\n    ImParseFormatSanitizeForPrinting(fmt_start, fmt_sanitized, IM_ARRAYSIZE(fmt_sanitized));\n    fmt_start = fmt_sanitized;\n\n    // Format value with our rounding, and read back\n    char v_str[64];\n    ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v);\n    const char* p = v_str;\n    while (*p == ' ')\n        p++;\n    v = (TYPE)ImAtof(p);\n\n    return v;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: DragScalar, DragFloat, DragInt, etc.\n//-------------------------------------------------------------------------\n// - DragBehaviorT<>() [Internal]\n// - DragBehavior() [Internal]\n// - DragScalar()\n// - DragScalarN()\n// - DragFloat()\n// - DragFloat2()\n// - DragFloat3()\n// - DragFloat4()\n// - DragFloatRange2()\n// - DragInt()\n// - DragInt2()\n// - DragInt3()\n// - DragInt4()\n// - DragIntRange2()\n//-------------------------------------------------------------------------\n\n// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls)\ntemplate<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>\nbool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;\n    const bool is_clamped = (v_min < v_max);\n    const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0;\n    const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);\n\n    // Default tweak speed\n    if (v_speed == 0.0f && is_clamped && (v_max - v_min < FLT_MAX))\n        v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio);\n\n    // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings\n    float adjust_delta = 0.0f;\n    if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))\n    {\n        adjust_delta = g.IO.MouseDelta[axis];\n        if (g.IO.KeyAlt)\n            adjust_delta *= 1.0f / 100.0f;\n        if (g.IO.KeyShift)\n            adjust_delta *= 10.0f;\n    }\n    else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)\n    {\n        const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0;\n        const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);\n        const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast);\n        const float tweak_factor = tweak_slow ? 1.0f / 1.0f : tweak_fast ? 10.0f : 1.0f;\n        adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor;\n        v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision));\n    }\n    adjust_delta *= v_speed;\n\n    // For vertical drag we currently assume that Up=higher value (like we do with vertical sliders). This may become a parameter.\n    if (axis == ImGuiAxis_Y)\n        adjust_delta = -adjust_delta;\n\n    // For logarithmic use our range is effectively 0..1 so scale the delta into that range\n    if (is_logarithmic && (v_max - v_min < FLT_MAX) && ((v_max - v_min) > 0.000001f)) // Epsilon to avoid /0\n        adjust_delta /= (float)(v_max - v_min);\n\n    // Clear current value on activation\n    // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300.\n    bool is_just_activated = g.ActiveIdIsJustActivated;\n    bool is_already_past_limits_and_pushing_outward = is_clamped && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f));\n    if (is_just_activated || is_already_past_limits_and_pushing_outward)\n    {\n        g.DragCurrentAccum = 0.0f;\n        g.DragCurrentAccumDirty = false;\n    }\n    else if (adjust_delta != 0.0f)\n    {\n        g.DragCurrentAccum += adjust_delta;\n        g.DragCurrentAccumDirty = true;\n    }\n\n    if (!g.DragCurrentAccumDirty)\n        return false;\n\n    TYPE v_cur = *v;\n    FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f;\n\n    float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true\n    const float zero_deadzone_halfsize = 0.0f; // Drag widgets have no deadzone (as it doesn't make sense)\n    if (is_logarithmic)\n    {\n        // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound.\n        const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1;\n        logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision);\n\n        // Convert to parametric space, apply delta, convert back\n        float v_old_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n        float v_new_parametric = v_old_parametric + g.DragCurrentAccum;\n        v_cur = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new_parametric, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n        v_old_ref_for_accum_remainder = v_old_parametric;\n    }\n    else\n    {\n        v_cur += (SIGNEDTYPE)g.DragCurrentAccum;\n    }\n\n    // Round to user desired precision based on format string\n    if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))\n        v_cur = RoundScalarWithFormatT<TYPE>(format, data_type, v_cur);\n\n    // Preserve remainder after rounding has been applied. This also allow slow tweaking of values.\n    g.DragCurrentAccumDirty = false;\n    if (is_logarithmic)\n    {\n        // Convert to parametric space, apply delta, convert back\n        float v_new_parametric = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_cur, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n        g.DragCurrentAccum -= (float)(v_new_parametric - v_old_ref_for_accum_remainder);\n    }\n    else\n    {\n        g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v);\n    }\n\n    // Lose zero sign for float/double\n    if (v_cur == (TYPE)-0)\n        v_cur = (TYPE)0;\n\n    // Clamp values (+ handle overflow/wrap-around for integer types)\n    if (*v != v_cur && is_clamped)\n    {\n        if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f && !is_floating_point))\n            v_cur = v_min;\n        if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f && !is_floating_point))\n            v_cur = v_max;\n    }\n\n    // Apply result\n    if (*v == v_cur)\n        return false;\n    *v = v_cur;\n    return true;\n}\n\nbool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)\n{\n    // Read imgui.cpp \"API BREAKING CHANGES\" section for 1.78 if you hit this assert.\n    IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && \"Invalid ImGuiSliderFlags flags! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.\");\n\n    ImGuiContext& g = *GImGui;\n    if (g.ActiveId == id)\n    {\n        // Those are the things we can do easily outside the DragBehaviorT<> template, saves code generation.\n        if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0])\n            ClearActiveID();\n        else if ((g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad) && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)\n            ClearActiveID();\n    }\n    if (g.ActiveId != id)\n        return false;\n    if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))\n        return false;\n\n    switch (data_type)\n    {\n    case ImGuiDataType_S8:     { ImS32 v32 = (ImS32)*(ImS8*)p_v;  bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS8*) p_min : IM_S8_MIN,  p_max ? *(const ImS8*)p_max  : IM_S8_MAX,  format, flags); if (r) *(ImS8*)p_v = (ImS8)v32; return r; }\n    case ImGuiDataType_U8:     { ImU32 v32 = (ImU32)*(ImU8*)p_v;  bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU8*) p_min : IM_U8_MIN,  p_max ? *(const ImU8*)p_max  : IM_U8_MAX,  format, flags); if (r) *(ImU8*)p_v = (ImU8)v32; return r; }\n    case ImGuiDataType_S16:    { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = DragBehaviorT<ImS32, ImS32, float>(ImGuiDataType_S32, &v32, v_speed, p_min ? *(const ImS16*)p_min : IM_S16_MIN, p_max ? *(const ImS16*)p_max : IM_S16_MAX, format, flags); if (r) *(ImS16*)p_v = (ImS16)v32; return r; }\n    case ImGuiDataType_U16:    { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = DragBehaviorT<ImU32, ImS32, float>(ImGuiDataType_U32, &v32, v_speed, p_min ? *(const ImU16*)p_min : IM_U16_MIN, p_max ? *(const ImU16*)p_max : IM_U16_MAX, format, flags); if (r) *(ImU16*)p_v = (ImU16)v32; return r; }\n    case ImGuiDataType_S32:    return DragBehaviorT<ImS32, ImS32, float >(data_type, (ImS32*)p_v,  v_speed, p_min ? *(const ImS32* )p_min : IM_S32_MIN, p_max ? *(const ImS32* )p_max : IM_S32_MAX, format, flags);\n    case ImGuiDataType_U32:    return DragBehaviorT<ImU32, ImS32, float >(data_type, (ImU32*)p_v,  v_speed, p_min ? *(const ImU32* )p_min : IM_U32_MIN, p_max ? *(const ImU32* )p_max : IM_U32_MAX, format, flags);\n    case ImGuiDataType_S64:    return DragBehaviorT<ImS64, ImS64, double>(data_type, (ImS64*)p_v,  v_speed, p_min ? *(const ImS64* )p_min : IM_S64_MIN, p_max ? *(const ImS64* )p_max : IM_S64_MAX, format, flags);\n    case ImGuiDataType_U64:    return DragBehaviorT<ImU64, ImS64, double>(data_type, (ImU64*)p_v,  v_speed, p_min ? *(const ImU64* )p_min : IM_U64_MIN, p_max ? *(const ImU64* )p_max : IM_U64_MAX, format, flags);\n    case ImGuiDataType_Float:  return DragBehaviorT<float, float, float >(data_type, (float*)p_v,  v_speed, p_min ? *(const float* )p_min : -FLT_MAX,   p_max ? *(const float* )p_max : FLT_MAX,    format, flags);\n    case ImGuiDataType_Double: return DragBehaviorT<double,double,double>(data_type, (double*)p_v, v_speed, p_min ? *(const double*)p_min : -DBL_MAX,   p_max ? *(const double*)p_max : DBL_MAX,    format, flags);\n    case ImGuiDataType_COUNT:  break;\n    }\n    IM_ASSERT(0);\n    return false;\n}\n\n// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a Drag widget, p_min and p_max are optional.\n// Read code of e.g. DragFloat(), DragInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.\nbool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    const float w = CalcItemWidth();\n\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));\n    const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));\n\n    const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))\n        return false;\n\n    // Default format string when passing NULL\n    if (format == NULL)\n        format = DataTypeGetInfo(data_type)->PrintFmt;\n\n    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);\n    bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);\n    if (!temp_input_is_active)\n    {\n        // Tabbing or CTRL-clicking on Drag turns it into an InputText\n        const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;\n        const bool clicked = hovered && IsMouseClicked(0, id);\n        const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id));\n        const bool make_active = (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id);\n        if (make_active && (clicked || double_clicked))\n            SetKeyOwner(ImGuiKey_MouseLeft, id);\n        if (make_active && temp_input_allowed)\n            if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))\n                temp_input_is_active = true;\n\n        // (Optional) simple click (without moving) turns Drag into an InputText\n        if (g.IO.ConfigDragClickToInputText && temp_input_allowed && !temp_input_is_active)\n            if (g.ActiveId == id && hovered && g.IO.MouseReleased[0] && !IsMouseDragPastThreshold(0, g.IO.MouseDragThreshold * DRAG_MOUSE_THRESHOLD_FACTOR))\n            {\n                g.NavActivateId = id;\n                g.NavActivateFlags = ImGuiActivateFlags_PreferInput;\n                temp_input_is_active = true;\n            }\n\n        if (make_active && !temp_input_is_active)\n        {\n            SetActiveID(id, window);\n            SetFocusID(id, window);\n            FocusWindow(window);\n            g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);\n        }\n    }\n\n    if (temp_input_is_active)\n    {\n        // Only clamp CTRL+Click input when ImGuiSliderFlags_AlwaysClamp is set\n        const bool is_clamp_input = (flags & ImGuiSliderFlags_AlwaysClamp) != 0 && (p_min == NULL || p_max == NULL || DataTypeCompare(data_type, p_min, p_max) < 0);\n        return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL);\n    }\n\n    // Draw frame\n    const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);\n    RenderNavHighlight(frame_bb, id);\n    RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding);\n\n    // Drag behavior\n    const bool value_changed = DragBehavior(id, data_type, p_data, v_speed, p_min, p_max, format, flags);\n    if (value_changed)\n        MarkItemEdited(id);\n\n    // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.\n    char value_buf[64];\n    const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);\n    if (g.LogEnabled)\n        LogSetNextTextDecoration(\"{\", \"}\");\n    RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));\n\n    if (label_size.x > 0.0f)\n        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));\n    return value_changed;\n}\n\nbool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    bool value_changed = false;\n    BeginGroup();\n    PushID(label);\n    PushMultiItemsWidths(components, CalcItemWidth());\n    size_t type_size = GDataTypeInfo[data_type].Size;\n    for (int i = 0; i < components; i++)\n    {\n        PushID(i);\n        if (i > 0)\n            SameLine(0, g.Style.ItemInnerSpacing.x);\n        value_changed |= DragScalar(\"\", data_type, p_data, v_speed, p_min, p_max, format, flags);\n        PopID();\n        PopItemWidth();\n        p_data = (void*)((char*)p_data + type_size);\n    }\n    PopID();\n\n    const char* label_end = FindRenderedTextEnd(label);\n    if (label != label_end)\n    {\n        SameLine(0, g.Style.ItemInnerSpacing.x);\n        TextEx(label, label_end);\n    }\n\n    EndGroup();\n    return value_changed;\n}\n\nbool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, flags);\n}\n\n// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this.\nbool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    PushID(label);\n    BeginGroup();\n    PushMultiItemsWidths(2, CalcItemWidth());\n\n    float min_min = (v_min >= v_max) ? -FLT_MAX : v_min;\n    float min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max);\n    ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0);\n    bool value_changed = DragScalar(\"##min\", ImGuiDataType_Float, v_current_min, v_speed, &min_min, &min_max, format, min_flags);\n    PopItemWidth();\n    SameLine(0, g.Style.ItemInnerSpacing.x);\n\n    float max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min);\n    float max_max = (v_min >= v_max) ? FLT_MAX : v_max;\n    ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0);\n    value_changed |= DragScalar(\"##max\", ImGuiDataType_Float, v_current_max, v_speed, &max_min, &max_max, format_max ? format_max : format, max_flags);\n    PopItemWidth();\n    SameLine(0, g.Style.ItemInnerSpacing.x);\n\n    TextEx(label, FindRenderedTextEnd(label));\n    EndGroup();\n    PopID();\n\n    return value_changed;\n}\n\n// NB: v_speed is float to allow adjusting the drag speed with more precision\nbool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format, flags);\n}\n\n// NB: You likely want to specify the ImGuiSliderFlags_AlwaysClamp when using this.\nbool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    PushID(label);\n    BeginGroup();\n    PushMultiItemsWidths(2, CalcItemWidth());\n\n    int min_min = (v_min >= v_max) ? INT_MIN : v_min;\n    int min_max = (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max);\n    ImGuiSliderFlags min_flags = flags | ((min_min == min_max) ? ImGuiSliderFlags_ReadOnly : 0);\n    bool value_changed = DragInt(\"##min\", v_current_min, v_speed, min_min, min_max, format, min_flags);\n    PopItemWidth();\n    SameLine(0, g.Style.ItemInnerSpacing.x);\n\n    int max_min = (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min);\n    int max_max = (v_min >= v_max) ? INT_MAX : v_max;\n    ImGuiSliderFlags max_flags = flags | ((max_min == max_max) ? ImGuiSliderFlags_ReadOnly : 0);\n    value_changed |= DragInt(\"##max\", v_current_max, v_speed, max_min, max_max, format_max ? format_max : format, max_flags);\n    PopItemWidth();\n    SameLine(0, g.Style.ItemInnerSpacing.x);\n\n    TextEx(label, FindRenderedTextEnd(label));\n    EndGroup();\n    PopID();\n\n    return value_changed;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc.\n//-------------------------------------------------------------------------\n// - ScaleRatioFromValueT<> [Internal]\n// - ScaleValueFromRatioT<> [Internal]\n// - SliderBehaviorT<>() [Internal]\n// - SliderBehavior() [Internal]\n// - SliderScalar()\n// - SliderScalarN()\n// - SliderFloat()\n// - SliderFloat2()\n// - SliderFloat3()\n// - SliderFloat4()\n// - SliderAngle()\n// - SliderInt()\n// - SliderInt2()\n// - SliderInt3()\n// - SliderInt4()\n// - VSliderScalar()\n// - VSliderFloat()\n// - VSliderInt()\n//-------------------------------------------------------------------------\n\n// Convert a value v in the output space of a slider into a parametric position on the slider itself (the logical opposite of ScaleValueFromRatioT)\ntemplate<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>\nfloat ImGui::ScaleRatioFromValueT(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize)\n{\n    if (v_min == v_max)\n        return 0.0f;\n    IM_UNUSED(data_type);\n\n    const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min);\n    if (is_logarithmic)\n    {\n        bool flipped = v_max < v_min;\n\n        if (flipped) // Handle the case where the range is backwards\n            ImSwap(v_min, v_max);\n\n        // Fudge min/max to avoid getting close to log(0)\n        FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min;\n        FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max;\n\n        // Awkward special cases - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon)\n        if ((v_min == 0.0f) && (v_max < 0.0f))\n            v_min_fudged = -logarithmic_zero_epsilon;\n        else if ((v_max == 0.0f) && (v_min < 0.0f))\n            v_max_fudged = -logarithmic_zero_epsilon;\n\n        float result;\n        if (v_clamped <= v_min_fudged)\n            result = 0.0f; // Workaround for values that are in-range but below our fudge\n        else if (v_clamped >= v_max_fudged)\n            result = 1.0f; // Workaround for values that are in-range but above our fudge\n        else if ((v_min * v_max) < 0.0f) // Range crosses zero, so split into two portions\n        {\n            float zero_point_center = (-(float)v_min) / ((float)v_max - (float)v_min); // The zero point in parametric space.  There's an argument we should take the logarithmic nature into account when calculating this, but for now this should do (and the most common case of a symmetrical range works fine)\n            float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize;\n            float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize;\n            if (v == 0.0f)\n                result = zero_point_center; // Special case for exactly zero\n            else if (v < 0.0f)\n                result = (1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(-v_min_fudged / logarithmic_zero_epsilon))) * zero_point_snap_L;\n            else\n                result = zero_point_snap_R + ((float)(ImLog((FLOATTYPE)v_clamped / logarithmic_zero_epsilon) / ImLog(v_max_fudged / logarithmic_zero_epsilon)) * (1.0f - zero_point_snap_R));\n        }\n        else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider\n            result = 1.0f - (float)(ImLog(-(FLOATTYPE)v_clamped / -v_max_fudged) / ImLog(-v_min_fudged / -v_max_fudged));\n        else\n            result = (float)(ImLog((FLOATTYPE)v_clamped / v_min_fudged) / ImLog(v_max_fudged / v_min_fudged));\n\n        return flipped ? (1.0f - result) : result;\n    }\n    else\n    {\n        // Linear slider\n        return (float)((FLOATTYPE)(SIGNEDTYPE)(v_clamped - v_min) / (FLOATTYPE)(SIGNEDTYPE)(v_max - v_min));\n    }\n}\n\n// Convert a parametric position on a slider into a value v in the output space (the logical opposite of ScaleRatioFromValueT)\ntemplate<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>\nTYPE ImGui::ScaleValueFromRatioT(ImGuiDataType data_type, float t, TYPE v_min, TYPE v_max, bool is_logarithmic, float logarithmic_zero_epsilon, float zero_deadzone_halfsize)\n{\n    // We special-case the extents because otherwise our logarithmic fudging can lead to \"mathematically correct\"\n    // but non-intuitive behaviors like a fully-left slider not actually reaching the minimum value. Also generally simpler.\n    if (t <= 0.0f || v_min == v_max)\n        return v_min;\n    if (t >= 1.0f)\n        return v_max;\n\n    TYPE result = (TYPE)0;\n    if (is_logarithmic)\n    {\n        // Fudge min/max to avoid getting silly results close to zero\n        FLOATTYPE v_min_fudged = (ImAbs((FLOATTYPE)v_min) < logarithmic_zero_epsilon) ? ((v_min < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_min;\n        FLOATTYPE v_max_fudged = (ImAbs((FLOATTYPE)v_max) < logarithmic_zero_epsilon) ? ((v_max < 0.0f) ? -logarithmic_zero_epsilon : logarithmic_zero_epsilon) : (FLOATTYPE)v_max;\n\n        const bool flipped = v_max < v_min; // Check if range is \"backwards\"\n        if (flipped)\n            ImSwap(v_min_fudged, v_max_fudged);\n\n        // Awkward special case - we need ranges of the form (-100 .. 0) to convert to (-100 .. -epsilon), not (-100 .. epsilon)\n        if ((v_max == 0.0f) && (v_min < 0.0f))\n            v_max_fudged = -logarithmic_zero_epsilon;\n\n        float t_with_flip = flipped ? (1.0f - t) : t; // t, but flipped if necessary to account for us flipping the range\n\n        if ((v_min * v_max) < 0.0f) // Range crosses zero, so we have to do this in two parts\n        {\n            float zero_point_center = (-(float)ImMin(v_min, v_max)) / ImAbs((float)v_max - (float)v_min); // The zero point in parametric space\n            float zero_point_snap_L = zero_point_center - zero_deadzone_halfsize;\n            float zero_point_snap_R = zero_point_center + zero_deadzone_halfsize;\n            if (t_with_flip >= zero_point_snap_L && t_with_flip <= zero_point_snap_R)\n                result = (TYPE)0.0f; // Special case to make getting exactly zero possible (the epsilon prevents it otherwise)\n            else if (t_with_flip < zero_point_center)\n                result = (TYPE)-(logarithmic_zero_epsilon * ImPow(-v_min_fudged / logarithmic_zero_epsilon, (FLOATTYPE)(1.0f - (t_with_flip / zero_point_snap_L))));\n            else\n                result = (TYPE)(logarithmic_zero_epsilon * ImPow(v_max_fudged / logarithmic_zero_epsilon, (FLOATTYPE)((t_with_flip - zero_point_snap_R) / (1.0f - zero_point_snap_R))));\n        }\n        else if ((v_min < 0.0f) || (v_max < 0.0f)) // Entirely negative slider\n            result = (TYPE)-(-v_max_fudged * ImPow(-v_min_fudged / -v_max_fudged, (FLOATTYPE)(1.0f - t_with_flip)));\n        else\n            result = (TYPE)(v_min_fudged * ImPow(v_max_fudged / v_min_fudged, (FLOATTYPE)t_with_flip));\n    }\n    else\n    {\n        // Linear slider\n        const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);\n        if (is_floating_point)\n        {\n            result = ImLerp(v_min, v_max, t);\n        }\n        else if (t < 1.0)\n        {\n            // - For integer values we want the clicking position to match the grab box so we round above\n            //   This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property..\n            // - Not doing a *1.0 multiply at the end of a range as it tends to be lossy. While absolute aiming at a large s64/u64\n            //   range is going to be imprecise anyway, with this check we at least make the edge values matches expected limits.\n            FLOATTYPE v_new_off_f = (SIGNEDTYPE)(v_max - v_min) * t;\n            result = (TYPE)((SIGNEDTYPE)v_min + (SIGNEDTYPE)(v_new_off_f + (FLOATTYPE)(v_min > v_max ? -0.5 : 0.5)));\n        }\n    }\n\n    return result;\n}\n\n// FIXME: Try to move more of the code into shared SliderBehavior()\ntemplate<typename TYPE, typename SIGNEDTYPE, typename FLOATTYPE>\nbool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb)\n{\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n\n    const ImGuiAxis axis = (flags & ImGuiSliderFlags_Vertical) ? ImGuiAxis_Y : ImGuiAxis_X;\n    const bool is_logarithmic = (flags & ImGuiSliderFlags_Logarithmic) != 0;\n    const bool is_floating_point = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double);\n    const float v_range_f = (float)(v_min < v_max ? v_max - v_min : v_min - v_max); // We don't need high precision for what we do with it.\n\n    // Calculate bounds\n    const float grab_padding = 2.0f; // FIXME: Should be part of style.\n    const float slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f;\n    float grab_sz = style.GrabMinSize;\n    if (!is_floating_point && v_range_f >= 0.0f)                         // v_range_f < 0 may happen on integer overflows\n        grab_sz = ImMax(slider_sz / (v_range_f + 1), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit\n    grab_sz = ImMin(grab_sz, slider_sz);\n    const float slider_usable_sz = slider_sz - grab_sz;\n    const float slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f;\n    const float slider_usable_pos_max = bb.Max[axis] - grab_padding - grab_sz * 0.5f;\n\n    float logarithmic_zero_epsilon = 0.0f; // Only valid when is_logarithmic is true\n    float zero_deadzone_halfsize = 0.0f; // Only valid when is_logarithmic is true\n    if (is_logarithmic)\n    {\n        // When using logarithmic sliders, we need to clamp to avoid hitting zero, but our choice of clamp value greatly affects slider precision. We attempt to use the specified precision to estimate a good lower bound.\n        const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 1;\n        logarithmic_zero_epsilon = ImPow(0.1f, (float)decimal_precision);\n        zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f);\n    }\n\n    // Process interacting with the slider\n    bool value_changed = false;\n    if (g.ActiveId == id)\n    {\n        bool set_new_value = false;\n        float clicked_t = 0.0f;\n        if (g.ActiveIdSource == ImGuiInputSource_Mouse)\n        {\n            if (!g.IO.MouseDown[0])\n            {\n                ClearActiveID();\n            }\n            else\n            {\n                const float mouse_abs_pos = g.IO.MousePos[axis];\n                if (g.ActiveIdIsJustActivated)\n                {\n                    float grab_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n                    if (axis == ImGuiAxis_Y)\n                        grab_t = 1.0f - grab_t;\n                    const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);\n                    const bool clicked_around_grab = (mouse_abs_pos >= grab_pos - grab_sz * 0.5f - 1.0f) && (mouse_abs_pos <= grab_pos + grab_sz * 0.5f + 1.0f); // No harm being extra generous here.\n                    g.SliderGrabClickOffset = (clicked_around_grab && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f;\n                }\n                if (slider_usable_sz > 0.0f)\n                    clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz);\n                if (axis == ImGuiAxis_Y)\n                    clicked_t = 1.0f - clicked_t;\n                set_new_value = true;\n            }\n        }\n        else if (g.ActiveIdSource == ImGuiInputSource_Keyboard || g.ActiveIdSource == ImGuiInputSource_Gamepad)\n        {\n            if (g.ActiveIdIsJustActivated)\n            {\n                g.SliderCurrentAccum = 0.0f; // Reset any stored nav delta upon activation\n                g.SliderCurrentAccumDirty = false;\n            }\n\n            float input_delta = (axis == ImGuiAxis_X) ? GetNavTweakPressedAmount(axis) : -GetNavTweakPressedAmount(axis);\n            if (input_delta != 0.0f)\n            {\n                const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow);\n                const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast);\n                const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0;\n                if (decimal_precision > 0)\n                {\n                    input_delta /= 100.0f;    // Gamepad/keyboard tweak speeds in % of slider bounds\n                    if (tweak_slow)\n                        input_delta /= 10.0f;\n                }\n                else\n                {\n                    if ((v_range_f >= -100.0f && v_range_f <= 100.0f && v_range_f != 0.0f) || tweak_slow)\n                        input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / v_range_f; // Gamepad/keyboard tweak speeds in integer steps\n                    else\n                        input_delta /= 100.0f;\n                }\n                if (tweak_fast)\n                    input_delta *= 10.0f;\n\n                g.SliderCurrentAccum += input_delta;\n                g.SliderCurrentAccumDirty = true;\n            }\n\n            float delta = g.SliderCurrentAccum;\n            if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated)\n            {\n                ClearActiveID();\n            }\n            else if (g.SliderCurrentAccumDirty)\n            {\n                clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n\n                if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits\n                {\n                    set_new_value = false;\n                    g.SliderCurrentAccum = 0.0f; // If pushing up against the limits, don't continue to accumulate\n                }\n                else\n                {\n                    set_new_value = true;\n                    float old_clicked_t = clicked_t;\n                    clicked_t = ImSaturate(clicked_t + delta);\n\n                    // Calculate what our \"new\" clicked_t will be, and thus how far we actually moved the slider, and subtract this from the accumulator\n                    TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n                    if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))\n                        v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);\n                    float new_clicked_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, v_new, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n\n                    if (delta > 0)\n                        g.SliderCurrentAccum -= ImMin(new_clicked_t - old_clicked_t, delta);\n                    else\n                        g.SliderCurrentAccum -= ImMax(new_clicked_t - old_clicked_t, delta);\n                }\n\n                g.SliderCurrentAccumDirty = false;\n            }\n        }\n\n        if (set_new_value)\n            if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly))\n                set_new_value = false;\n\n        if (set_new_value)\n        {\n            TYPE v_new = ScaleValueFromRatioT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, clicked_t, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n\n            // Round to user desired precision based on format string\n            if (is_floating_point && !(flags & ImGuiSliderFlags_NoRoundToFormat))\n                v_new = RoundScalarWithFormatT<TYPE>(format, data_type, v_new);\n\n            // Apply result\n            if (*v != v_new)\n            {\n                *v = v_new;\n                value_changed = true;\n            }\n        }\n    }\n\n    if (slider_sz < 1.0f)\n    {\n        *out_grab_bb = ImRect(bb.Min, bb.Min);\n    }\n    else\n    {\n        // Output grab position so it can be displayed by the caller\n        float grab_t = ScaleRatioFromValueT<TYPE, SIGNEDTYPE, FLOATTYPE>(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize);\n        if (axis == ImGuiAxis_Y)\n            grab_t = 1.0f - grab_t;\n        const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t);\n        if (axis == ImGuiAxis_X)\n            *out_grab_bb = ImRect(grab_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5f, bb.Max.y - grab_padding);\n        else\n            *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f);\n    }\n\n    return value_changed;\n}\n\n// For 32-bit and larger types, slider bounds are limited to half the natural type range.\n// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2 will be ok.\n// It would be possible to lift that limitation with some work but it doesn't seem to be worth it for sliders.\nbool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb)\n{\n    // Read imgui.cpp \"API BREAKING CHANGES\" section for 1.78 if you hit this assert.\n    IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && \"Invalid ImGuiSliderFlags flag!  Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead.\");\n\n    switch (data_type)\n    {\n    case ImGuiDataType_S8:  { ImS32 v32 = (ImS32)*(ImS8*)p_v;  bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS8*)p_min,  *(const ImS8*)p_max,  format, flags, out_grab_bb); if (r) *(ImS8*)p_v  = (ImS8)v32;  return r; }\n    case ImGuiDataType_U8:  { ImU32 v32 = (ImU32)*(ImU8*)p_v;  bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU8*)p_min,  *(const ImU8*)p_max,  format, flags, out_grab_bb); if (r) *(ImU8*)p_v  = (ImU8)v32;  return r; }\n    case ImGuiDataType_S16: { ImS32 v32 = (ImS32)*(ImS16*)p_v; bool r = SliderBehaviorT<ImS32, ImS32, float>(bb, id, ImGuiDataType_S32, &v32, *(const ImS16*)p_min, *(const ImS16*)p_max, format, flags, out_grab_bb); if (r) *(ImS16*)p_v = (ImS16)v32; return r; }\n    case ImGuiDataType_U16: { ImU32 v32 = (ImU32)*(ImU16*)p_v; bool r = SliderBehaviorT<ImU32, ImS32, float>(bb, id, ImGuiDataType_U32, &v32, *(const ImU16*)p_min, *(const ImU16*)p_max, format, flags, out_grab_bb); if (r) *(ImU16*)p_v = (ImU16)v32; return r; }\n    case ImGuiDataType_S32:\n        IM_ASSERT(*(const ImS32*)p_min >= IM_S32_MIN / 2 && *(const ImS32*)p_max <= IM_S32_MAX / 2);\n        return SliderBehaviorT<ImS32, ImS32, float >(bb, id, data_type, (ImS32*)p_v,  *(const ImS32*)p_min,  *(const ImS32*)p_max,  format, flags, out_grab_bb);\n    case ImGuiDataType_U32:\n        IM_ASSERT(*(const ImU32*)p_max <= IM_U32_MAX / 2);\n        return SliderBehaviorT<ImU32, ImS32, float >(bb, id, data_type, (ImU32*)p_v,  *(const ImU32*)p_min,  *(const ImU32*)p_max,  format, flags, out_grab_bb);\n    case ImGuiDataType_S64:\n        IM_ASSERT(*(const ImS64*)p_min >= IM_S64_MIN / 2 && *(const ImS64*)p_max <= IM_S64_MAX / 2);\n        return SliderBehaviorT<ImS64, ImS64, double>(bb, id, data_type, (ImS64*)p_v,  *(const ImS64*)p_min,  *(const ImS64*)p_max,  format, flags, out_grab_bb);\n    case ImGuiDataType_U64:\n        IM_ASSERT(*(const ImU64*)p_max <= IM_U64_MAX / 2);\n        return SliderBehaviorT<ImU64, ImS64, double>(bb, id, data_type, (ImU64*)p_v,  *(const ImU64*)p_min,  *(const ImU64*)p_max,  format, flags, out_grab_bb);\n    case ImGuiDataType_Float:\n        IM_ASSERT(*(const float*)p_min >= -FLT_MAX / 2.0f && *(const float*)p_max <= FLT_MAX / 2.0f);\n        return SliderBehaviorT<float, float, float >(bb, id, data_type, (float*)p_v,  *(const float*)p_min,  *(const float*)p_max,  format, flags, out_grab_bb);\n    case ImGuiDataType_Double:\n        IM_ASSERT(*(const double*)p_min >= -DBL_MAX / 2.0f && *(const double*)p_max <= DBL_MAX / 2.0f);\n        return SliderBehaviorT<double, double, double>(bb, id, data_type, (double*)p_v, *(const double*)p_min, *(const double*)p_max, format, flags, out_grab_bb);\n    case ImGuiDataType_COUNT: break;\n    }\n    IM_ASSERT(0);\n    return false;\n}\n\n// Note: p_data, p_min and p_max are _pointers_ to a memory address holding the data. For a slider, they are all required.\n// Read code of e.g. SliderFloat(), SliderInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.\nbool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    const float w = CalcItemWidth();\n\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y * 2.0f));\n    const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));\n\n    const bool temp_input_allowed = (flags & ImGuiSliderFlags_NoInput) == 0;\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, id, &frame_bb, temp_input_allowed ? ImGuiItemFlags_Inputable : 0))\n        return false;\n\n    // Default format string when passing NULL\n    if (format == NULL)\n        format = DataTypeGetInfo(data_type)->PrintFmt;\n\n    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);\n    bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id);\n    if (!temp_input_is_active)\n    {\n        // Tabbing or CTRL-clicking on Slider turns it into an input box\n        const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;\n        const bool clicked = hovered && IsMouseClicked(0, id);\n        const bool make_active = (input_requested_by_tabbing || clicked || g.NavActivateId == id);\n        if (make_active && clicked)\n            SetKeyOwner(ImGuiKey_MouseLeft, id);\n        if (make_active && temp_input_allowed)\n            if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || (g.NavActivateId == id && (g.NavActivateFlags & ImGuiActivateFlags_PreferInput)))\n                temp_input_is_active = true;\n\n        if (make_active && !temp_input_is_active)\n        {\n            SetActiveID(id, window);\n            SetFocusID(id, window);\n            FocusWindow(window);\n            g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);\n        }\n    }\n\n    if (temp_input_is_active)\n    {\n        // Only clamp CTRL+Click input when ImGuiSliderFlags_AlwaysClamp is set\n        const bool is_clamp_input = (flags & ImGuiSliderFlags_AlwaysClamp) != 0;\n        return TempInputScalar(frame_bb, id, label, data_type, p_data, format, is_clamp_input ? p_min : NULL, is_clamp_input ? p_max : NULL);\n    }\n\n    // Draw frame\n    const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);\n    RenderNavHighlight(frame_bb, id);\n    RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);\n\n    // Slider behavior\n    ImRect grab_bb;\n    const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags, &grab_bb);\n    if (value_changed)\n        MarkItemEdited(id);\n\n    // Render grab\n    if (grab_bb.Max.x > grab_bb.Min.x)\n        window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);\n\n    // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.\n    char value_buf[64];\n    const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);\n    if (g.LogEnabled)\n        LogSetNextTextDecoration(\"{\", \"}\");\n    RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f));\n\n    if (label_size.x > 0.0f)\n        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (temp_input_allowed ? ImGuiItemStatusFlags_Inputable : 0));\n    return value_changed;\n}\n\n// Add multiple sliders on 1 line for compact edition of multiple components\nbool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    bool value_changed = false;\n    BeginGroup();\n    PushID(label);\n    PushMultiItemsWidths(components, CalcItemWidth());\n    size_t type_size = GDataTypeInfo[data_type].Size;\n    for (int i = 0; i < components; i++)\n    {\n        PushID(i);\n        if (i > 0)\n            SameLine(0, g.Style.ItemInnerSpacing.x);\n        value_changed |= SliderScalar(\"\", data_type, v, v_min, v_max, format, flags);\n        PopID();\n        PopItemWidth();\n        v = (void*)((char*)v + type_size);\n    }\n    PopID();\n\n    const char* label_end = FindRenderedTextEnd(label);\n    if (label != label_end)\n    {\n        SameLine(0, g.Style.ItemInnerSpacing.x);\n        TextEx(label, label_end);\n    }\n\n    EndGroup();\n    return value_changed;\n}\n\nbool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags)\n{\n    if (format == NULL)\n        format = \"%.0f deg\";\n    float v_deg = (*v_rad) * 360.0f / (2 * IM_PI);\n    bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, format, flags);\n    *v_rad = v_deg * (2 * IM_PI) / 360.0f;\n    return value_changed;\n}\n\nbool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size);\n    const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));\n\n    ItemSize(bb, style.FramePadding.y);\n    if (!ItemAdd(frame_bb, id))\n        return false;\n\n    // Default format string when passing NULL\n    if (format == NULL)\n        format = DataTypeGetInfo(data_type)->PrintFmt;\n\n    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);\n    const bool clicked = hovered && IsMouseClicked(0, id);\n    if (clicked || g.NavActivateId == id)\n    {\n        if (clicked)\n            SetKeyOwner(ImGuiKey_MouseLeft, id);\n        SetActiveID(id, window);\n        SetFocusID(id, window);\n        FocusWindow(window);\n        g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);\n    }\n\n    // Draw frame\n    const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);\n    RenderNavHighlight(frame_bb, id);\n    RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, g.Style.FrameRounding);\n\n    // Slider behavior\n    ImRect grab_bb;\n    const bool value_changed = SliderBehavior(frame_bb, id, data_type, p_data, p_min, p_max, format, flags | ImGuiSliderFlags_Vertical, &grab_bb);\n    if (value_changed)\n        MarkItemEdited(id);\n\n    // Render grab\n    if (grab_bb.Max.y > grab_bb.Min.y)\n        window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding);\n\n    // Display value using user-provided display format so user can add prefix/suffix/decorations to the value.\n    // For the vertical slider we allow centered text to overlap the frame padding\n    char value_buf[64];\n    const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, p_data, format);\n    RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.0f));\n    if (label_size.x > 0.0f)\n        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);\n\n    return value_changed;\n}\n\nbool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, flags);\n}\n\nbool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags)\n{\n    return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format, flags);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc.\n//-------------------------------------------------------------------------\n// - ImParseFormatFindStart() [Internal]\n// - ImParseFormatFindEnd() [Internal]\n// - ImParseFormatTrimDecorations() [Internal]\n// - ImParseFormatSanitizeForPrinting() [Internal]\n// - ImParseFormatSanitizeForScanning() [Internal]\n// - ImParseFormatPrecision() [Internal]\n// - TempInputTextScalar() [Internal]\n// - InputScalar()\n// - InputScalarN()\n// - InputFloat()\n// - InputFloat2()\n// - InputFloat3()\n// - InputFloat4()\n// - InputInt()\n// - InputInt2()\n// - InputInt3()\n// - InputInt4()\n// - InputDouble()\n//-------------------------------------------------------------------------\n\n// We don't use strchr() because our strings are usually very short and often start with '%'\nconst char* ImParseFormatFindStart(const char* fmt)\n{\n    while (char c = fmt[0])\n    {\n        if (c == '%' && fmt[1] != '%')\n            return fmt;\n        else if (c == '%')\n            fmt++;\n        fmt++;\n    }\n    return fmt;\n}\n\nconst char* ImParseFormatFindEnd(const char* fmt)\n{\n    // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format.\n    if (fmt[0] != '%')\n        return fmt;\n    const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A'));\n    const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a'));\n    for (char c; (c = *fmt) != 0; fmt++)\n    {\n        if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0)\n            return fmt + 1;\n        if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0)\n            return fmt + 1;\n    }\n    return fmt;\n}\n\n// Extract the format out of a format string with leading or trailing decorations\n//  fmt = \"blah blah\"  -> return \"\"\n//  fmt = \"%.3f\"       -> return fmt\n//  fmt = \"hello %.3f\" -> return fmt + 6\n//  fmt = \"%.3f hello\" -> return buf written with \"%.3f\"\nconst char* ImParseFormatTrimDecorations(const char* fmt, char* buf, size_t buf_size)\n{\n    const char* fmt_start = ImParseFormatFindStart(fmt);\n    if (fmt_start[0] != '%')\n        return \"\";\n    const char* fmt_end = ImParseFormatFindEnd(fmt_start);\n    if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data.\n        return fmt_start;\n    ImStrncpy(buf, fmt_start, ImMin((size_t)(fmt_end - fmt_start) + 1, buf_size));\n    return buf;\n}\n\n// Sanitize format\n// - Zero terminate so extra characters after format (e.g. \"%f123\") don't confuse atof/atoi\n// - stb_sprintf.h supports several new modifiers which format numbers in a way that also makes them incompatible atof/atoi.\nvoid ImParseFormatSanitizeForPrinting(const char* fmt_in, char* fmt_out, size_t fmt_out_size)\n{\n    const char* fmt_end = ImParseFormatFindEnd(fmt_in);\n    IM_UNUSED(fmt_out_size);\n    IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!\n    while (fmt_in < fmt_end)\n    {\n        char c = *fmt_in++;\n        if (c != '\\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.\n            *(fmt_out++) = c;\n    }\n    *fmt_out = 0; // Zero-terminate\n}\n\n// - For scanning we need to remove all width and precision fields and flags \"%+3.7f\" -> \"%f\". BUT don't strip types like \"%I64d\" which includes digits. ! \"%07I64d\" -> \"%I64d\"\nconst char* ImParseFormatSanitizeForScanning(const char* fmt_in, char* fmt_out, size_t fmt_out_size)\n{\n    const char* fmt_end = ImParseFormatFindEnd(fmt_in);\n    const char* fmt_out_begin = fmt_out;\n    IM_UNUSED(fmt_out_size);\n    IM_ASSERT((size_t)(fmt_end - fmt_in + 1) < fmt_out_size); // Format is too long, let us know if this happens to you!\n    bool has_type = false;\n    while (fmt_in < fmt_end)\n    {\n        char c = *fmt_in++;\n        if (!has_type && ((c >= '0' && c <= '9') || c == '.' || c == '+' || c == '#'))\n            continue;\n        has_type |= ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); // Stop skipping digits\n        if (c != '\\'' && c != '$' && c != '_') // Custom flags provided by stb_sprintf.h. POSIX 2008 also supports '.\n            *(fmt_out++) = c;\n    }\n    *fmt_out = 0; // Zero-terminate\n    return fmt_out_begin;\n}\n\ntemplate<typename TYPE>\nstatic const char* ImAtoi(const char* src, TYPE* output)\n{\n    int negative = 0;\n    if (*src == '-') { negative = 1; src++; }\n    if (*src == '+') { src++; }\n    TYPE v = 0;\n    while (*src >= '0' && *src <= '9')\n        v = (v * 10) + (*src++ - '0');\n    *output = negative ? -v : v;\n    return src;\n}\n\n// Parse display precision back from the display format string\n// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed.\nint ImParseFormatPrecision(const char* fmt, int default_precision)\n{\n    fmt = ImParseFormatFindStart(fmt);\n    if (fmt[0] != '%')\n        return default_precision;\n    fmt++;\n    while (*fmt >= '0' && *fmt <= '9')\n        fmt++;\n    int precision = INT_MAX;\n    if (*fmt == '.')\n    {\n        fmt = ImAtoi<int>(fmt + 1, &precision);\n        if (precision < 0 || precision > 99)\n            precision = default_precision;\n    }\n    if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation\n        precision = -1;\n    if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX)\n        precision = -1;\n    return (precision == INT_MAX) ? default_precision : precision;\n}\n\n// Create text input in place of another active widget (e.g. used when doing a CTRL+Click on drag/slider widgets)\n// FIXME: Facilitate using this in variety of other situations.\nbool ImGui::TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags)\n{\n    // On the first frame, g.TempInputTextId == 0, then on subsequent frames it becomes == id.\n    // We clear ActiveID on the first frame to allow the InputText() taking it back.\n    ImGuiContext& g = *GImGui;\n    const bool init = (g.TempInputId != id);\n    if (init)\n        ClearActiveID();\n\n    g.CurrentWindow->DC.CursorPos = bb.Min;\n    bool value_changed = InputTextEx(label, NULL, buf, buf_size, bb.GetSize(), flags | ImGuiInputTextFlags_MergedItem);\n    if (init)\n    {\n        // First frame we started displaying the InputText widget, we expect it to take the active id.\n        IM_ASSERT(g.ActiveId == id);\n        g.TempInputId = g.ActiveId;\n    }\n    return value_changed;\n}\n\nstatic inline ImGuiInputTextFlags InputScalar_DefaultCharsFilter(ImGuiDataType data_type, const char* format)\n{\n    if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double)\n        return ImGuiInputTextFlags_CharsScientific;\n    const char format_last_char = format[0] ? format[strlen(format) - 1] : 0;\n    return (format_last_char == 'x' || format_last_char == 'X') ? ImGuiInputTextFlags_CharsHexadecimal : ImGuiInputTextFlags_CharsDecimal;\n}\n\n// Note that Drag/Slider functions are only forwarding the min/max values clamping values if the ImGuiSliderFlags_AlwaysClamp flag is set!\n// This is intended: this way we allow CTRL+Click manual input to set a value out of bounds, for maximum flexibility.\n// However this may not be ideal for all uses, as some user code may break on out of bound values.\nbool ImGui::TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min, const void* p_clamp_max)\n{\n    // FIXME: May need to clarify display behavior if format doesn't contain %.\n    // \"%d\" -> \"%d\" / \"There are %d items\" -> \"%d\" / \"items\" -> \"%d\" (fallback). Also see #6405\n    const ImGuiDataTypeInfo* type_info = DataTypeGetInfo(data_type);\n    char fmt_buf[32];\n    char data_buf[32];\n    format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf));\n    if (format[0] == 0)\n        format = type_info->PrintFmt;\n    DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, p_data, format);\n    ImStrTrimBlanks(data_buf);\n\n    ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited;\n    flags |= InputScalar_DefaultCharsFilter(data_type, format);\n\n    bool value_changed = false;\n    if (TempInputText(bb, id, label, data_buf, IM_ARRAYSIZE(data_buf), flags))\n    {\n        // Backup old value\n        size_t data_type_size = type_info->Size;\n        ImGuiDataTypeTempStorage data_backup;\n        memcpy(&data_backup, p_data, data_type_size);\n\n        // Apply new value (or operations) then clamp\n        DataTypeApplyFromText(data_buf, data_type, p_data, format);\n        if (p_clamp_min || p_clamp_max)\n        {\n            if (p_clamp_min && p_clamp_max && DataTypeCompare(data_type, p_clamp_min, p_clamp_max) > 0)\n                ImSwap(p_clamp_min, p_clamp_max);\n            DataTypeClamp(data_type, p_data, p_clamp_min, p_clamp_max);\n        }\n\n        // Only mark as edited if new value is different\n        value_changed = memcmp(&data_backup, p_data, data_type_size) != 0;\n        if (value_changed)\n            MarkItemEdited(id);\n    }\n    return value_changed;\n}\n\n// Note: p_data, p_step, p_step_fast are _pointers_ to a memory address holding the data. For an Input widget, p_step and p_step_fast are optional.\n// Read code of e.g. InputFloat(), InputInt() etc. or examples in 'Demo->Widgets->Data Types' to understand how to use this function directly.\nbool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n\n    if (format == NULL)\n        format = DataTypeGetInfo(data_type)->PrintFmt;\n\n    char buf[64];\n    DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, p_data, format);\n\n    // Testing ActiveId as a minor optimization as filtering is not needed until active\n    if (g.ActiveId == 0 && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0)\n        flags |= InputScalar_DefaultCharsFilter(data_type, format);\n    flags |= ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoMarkEdited; // We call MarkItemEdited() ourselves by comparing the actual data rather than the string.\n\n    bool value_changed = false;\n    if (p_step == NULL)\n    {\n        if (InputText(label, buf, IM_ARRAYSIZE(buf), flags))\n            value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);\n    }\n    else\n    {\n        const float button_size = GetFrameHeight();\n\n        BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive()\n        PushID(label);\n        SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));\n        if (InputText(\"\", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + \"\" gives us the expected ID from outside point of view\n            value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);\n        IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);\n\n        // Step buttons\n        const ImVec2 backup_frame_padding = style.FramePadding;\n        style.FramePadding.x = style.FramePadding.y;\n        ImGuiButtonFlags button_flags = ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups;\n        if (flags & ImGuiInputTextFlags_ReadOnly)\n            BeginDisabled();\n        SameLine(0, style.ItemInnerSpacing.x);\n        if (ButtonEx(\"-\", ImVec2(button_size, button_size), button_flags))\n        {\n            DataTypeApplyOp(data_type, '-', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);\n            value_changed = true;\n        }\n        SameLine(0, style.ItemInnerSpacing.x);\n        if (ButtonEx(\"+\", ImVec2(button_size, button_size), button_flags))\n        {\n            DataTypeApplyOp(data_type, '+', p_data, p_data, g.IO.KeyCtrl && p_step_fast ? p_step_fast : p_step);\n            value_changed = true;\n        }\n        if (flags & ImGuiInputTextFlags_ReadOnly)\n            EndDisabled();\n\n        const char* label_end = FindRenderedTextEnd(label);\n        if (label != label_end)\n        {\n            SameLine(0, style.ItemInnerSpacing.x);\n            TextEx(label, label_end);\n        }\n        style.FramePadding = backup_frame_padding;\n\n        PopID();\n        EndGroup();\n    }\n    if (value_changed)\n        MarkItemEdited(g.LastItemData.ID);\n\n    return value_changed;\n}\n\nbool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_step, const void* p_step_fast, const char* format, ImGuiInputTextFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    bool value_changed = false;\n    BeginGroup();\n    PushID(label);\n    PushMultiItemsWidths(components, CalcItemWidth());\n    size_t type_size = GDataTypeInfo[data_type].Size;\n    for (int i = 0; i < components; i++)\n    {\n        PushID(i);\n        if (i > 0)\n            SameLine(0, g.Style.ItemInnerSpacing.x);\n        value_changed |= InputScalar(\"\", data_type, p_data, p_step, p_step_fast, format, flags);\n        PopID();\n        PopItemWidth();\n        p_data = (void*)((char*)p_data + type_size);\n    }\n    PopID();\n\n    const char* label_end = FindRenderedTextEnd(label);\n    if (label != label_end)\n    {\n        SameLine(0.0f, g.Style.ItemInnerSpacing.x);\n        TextEx(label, label_end);\n    }\n\n    EndGroup();\n    return value_changed;\n}\n\nbool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags flags)\n{\n    flags |= ImGuiInputTextFlags_CharsScientific;\n    return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step > 0.0f ? &step : NULL), (void*)(step_fast > 0.0f ? &step_fast : NULL), format, flags);\n}\n\nbool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, flags);\n}\n\nbool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, flags);\n}\n\nbool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, flags);\n}\n\nbool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags flags)\n{\n    // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes.\n    const char* format = (flags & ImGuiInputTextFlags_CharsHexadecimal) ? \"%08X\" : \"%d\";\n    return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step > 0 ? &step : NULL), (void*)(step_fast > 0 ? &step_fast : NULL), format, flags);\n}\n\nbool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, \"%d\", flags);\n}\n\nbool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, \"%d\", flags);\n}\n\nbool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags flags)\n{\n    return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, \"%d\", flags);\n}\n\nbool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags flags)\n{\n    flags |= ImGuiInputTextFlags_CharsScientific;\n    return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step > 0.0 ? &step : NULL), (void*)(step_fast > 0.0 ? &step_fast : NULL), format, flags);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: InputText, InputTextMultiline, InputTextWithHint\n//-------------------------------------------------------------------------\n// - InputText()\n// - InputTextWithHint()\n// - InputTextMultiline()\n// - InputTextGetCharInfo() [Internal]\n// - InputTextReindexLines() [Internal]\n// - InputTextReindexLinesRange() [Internal]\n// - InputTextEx() [Internal]\n// - DebugNodeInputTextState() [Internal]\n//-------------------------------------------------------------------------\n\nbool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)\n{\n    IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline()\n    return InputTextEx(label, NULL, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);\n}\n\nbool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)\n{\n    return InputTextEx(label, NULL, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data);\n}\n\nbool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)\n{\n    IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() or  InputTextEx() manually if you need multi-line + hint.\n    return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data);\n}\n\nstatic int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end)\n{\n    int line_count = 0;\n    const char* s = text_begin;\n    while (char c = *s++) // We are only matching for \\n so we can ignore UTF-8 decoding\n        if (c == '\\n')\n            line_count++;\n    s--;\n    if (s[0] != '\\n' && s[0] != '\\r')\n        line_count++;\n    *out_text_end = s;\n    return line_count;\n}\n\nstatic ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line)\n{\n    ImGuiContext& g = *ctx;\n    ImFont* font = g.Font;\n    const float line_height = g.FontSize;\n    const float scale = line_height / font->FontSize;\n\n    ImVec2 text_size = ImVec2(0, 0);\n    float line_width = 0.0f;\n\n    const ImWchar* s = text_begin;\n    while (s < text_end)\n    {\n        unsigned int c = (unsigned int)(*s++);\n        if (c == '\\n')\n        {\n            text_size.x = ImMax(text_size.x, line_width);\n            text_size.y += line_height;\n            line_width = 0.0f;\n            if (stop_on_new_line)\n                break;\n            continue;\n        }\n        if (c == '\\r')\n            continue;\n\n        const float char_width = font->GetCharAdvance((ImWchar)c) * scale;\n        line_width += char_width;\n    }\n\n    if (text_size.x < line_width)\n        text_size.x = line_width;\n\n    if (out_offset)\n        *out_offset = ImVec2(line_width, text_size.y + line_height);  // offset allow for the possibility of sitting after a trailing \\n\n\n    if (line_width > 0 || text_size.y == 0.0f)                        // whereas size.y will ignore the trailing \\n\n        text_size.y += line_height;\n\n    if (remaining)\n        *remaining = s;\n\n    return text_size;\n}\n\n// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar)\nnamespace ImStb\n{\n\nstatic int     STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj)                             { return obj->CurLenW; }\nstatic ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx)                      { IM_ASSERT(idx <= obj->CurLenW); return obj->TextW[idx]; }\nstatic float   STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx)  { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); }\nstatic int     STB_TEXTEDIT_KEYTOTEXT(int key)                                                    { return key >= 0x200000 ? 0 : key; }\nstatic ImWchar STB_TEXTEDIT_NEWLINE = '\\n';\nstatic void    STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx)\n{\n    const ImWchar* text = obj->TextW.Data;\n    const ImWchar* text_remaining = NULL;\n    const ImVec2 size = InputTextCalcTextSizeW(obj->Ctx, text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true);\n    r->x0 = 0.0f;\n    r->x1 = size.x;\n    r->baseline_y_delta = size.y;\n    r->ymin = 0.0f;\n    r->ymax = size.y;\n    r->num_chars = (int)(text_remaining - (text + line_start_idx));\n}\n\nstatic bool is_separator(unsigned int c)\n{\n    return c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|' || c=='\\n' || c=='\\r' || c=='.' || c=='!';\n}\n\nstatic int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx)\n{\n    // When ImGuiInputTextFlags_Password is set, we don't want actions such as CTRL+Arrow to leak the fact that underlying data are blanks or separators.\n    if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)\n        return 0;\n\n    bool prev_white = ImCharIsBlankW(obj->TextW[idx - 1]);\n    bool prev_separ = is_separator(obj->TextW[idx - 1]);\n    bool curr_white = ImCharIsBlankW(obj->TextW[idx]);\n    bool curr_separ = is_separator(obj->TextW[idx]);\n    return ((prev_white || prev_separ) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);\n}\nstatic int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx)\n{\n    if ((obj->Flags & ImGuiInputTextFlags_Password) || idx <= 0)\n        return 0;\n\n    bool prev_white = ImCharIsBlankW(obj->TextW[idx]);\n    bool prev_separ = is_separator(obj->TextW[idx]);\n    bool curr_white = ImCharIsBlankW(obj->TextW[idx - 1]);\n    bool curr_separ = is_separator(obj->TextW[idx - 1]);\n    return ((prev_white) && !(curr_separ || curr_white)) || (curr_separ && !prev_separ);\n}\nstatic int  STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx)   { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; }\nstatic int  STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx)   { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; }\nstatic int  STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx)   { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; }\nstatic int  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx)  { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); }\n#define STB_TEXTEDIT_MOVEWORDLEFT   STB_TEXTEDIT_MOVEWORDLEFT_IMPL  // They need to be #define for stb_textedit.h\n#define STB_TEXTEDIT_MOVEWORDRIGHT  STB_TEXTEDIT_MOVEWORDRIGHT_IMPL\n\nstatic void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n)\n{\n    ImWchar* dst = obj->TextW.Data + pos;\n\n    // We maintain our buffer length in both UTF-8 and wchar formats\n    obj->Edited = true;\n    obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n);\n    obj->CurLenW -= n;\n\n    // Offset remaining text (FIXME-OPT: Use memmove)\n    const ImWchar* src = obj->TextW.Data + pos + n;\n    while (ImWchar c = *src++)\n        *dst++ = c;\n    *dst = '\\0';\n}\n\nstatic bool STB_TEXTEDIT_INSERTCHARS(ImGuiInputTextState* obj, int pos, const ImWchar* new_text, int new_text_len)\n{\n    const bool is_resizable = (obj->Flags & ImGuiInputTextFlags_CallbackResize) != 0;\n    const int text_len = obj->CurLenW;\n    IM_ASSERT(pos <= text_len);\n\n    const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len);\n    if (!is_resizable && (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufCapacityA))\n        return false;\n\n    // Grow internal buffer if needed\n    if (new_text_len + text_len + 1 > obj->TextW.Size)\n    {\n        if (!is_resizable)\n            return false;\n        IM_ASSERT(text_len < obj->TextW.Size);\n        obj->TextW.resize(text_len + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1);\n    }\n\n    ImWchar* text = obj->TextW.Data;\n    if (pos != text_len)\n        memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar));\n    memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar));\n\n    obj->Edited = true;\n    obj->CurLenW += new_text_len;\n    obj->CurLenA += new_text_len_utf8;\n    obj->TextW[obj->CurLenW] = '\\0';\n\n    return true;\n}\n\n// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols)\n#define STB_TEXTEDIT_K_LEFT         0x200000 // keyboard input to move cursor left\n#define STB_TEXTEDIT_K_RIGHT        0x200001 // keyboard input to move cursor right\n#define STB_TEXTEDIT_K_UP           0x200002 // keyboard input to move cursor up\n#define STB_TEXTEDIT_K_DOWN         0x200003 // keyboard input to move cursor down\n#define STB_TEXTEDIT_K_LINESTART    0x200004 // keyboard input to move cursor to start of line\n#define STB_TEXTEDIT_K_LINEEND      0x200005 // keyboard input to move cursor to end of line\n#define STB_TEXTEDIT_K_TEXTSTART    0x200006 // keyboard input to move cursor to start of text\n#define STB_TEXTEDIT_K_TEXTEND      0x200007 // keyboard input to move cursor to end of text\n#define STB_TEXTEDIT_K_DELETE       0x200008 // keyboard input to delete selection or character under cursor\n#define STB_TEXTEDIT_K_BACKSPACE    0x200009 // keyboard input to delete selection or character left of cursor\n#define STB_TEXTEDIT_K_UNDO         0x20000A // keyboard input to perform undo\n#define STB_TEXTEDIT_K_REDO         0x20000B // keyboard input to perform redo\n#define STB_TEXTEDIT_K_WORDLEFT     0x20000C // keyboard input to move cursor left one word\n#define STB_TEXTEDIT_K_WORDRIGHT    0x20000D // keyboard input to move cursor right one word\n#define STB_TEXTEDIT_K_PGUP         0x20000E // keyboard input to move cursor up a page\n#define STB_TEXTEDIT_K_PGDOWN       0x20000F // keyboard input to move cursor down a page\n#define STB_TEXTEDIT_K_SHIFT        0x400000\n\n#define STB_TEXTEDIT_IMPLEMENTATION\n#define STB_TEXTEDIT_memmove memmove\n#include \"imstb_textedit.h\"\n\n// stb_textedit internally allows for a single undo record to do addition and deletion, but somehow, calling\n// the stb_textedit_paste() function creates two separate records, so we perform it manually. (FIXME: Report to nothings/stb?)\nstatic void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* state, const STB_TEXTEDIT_CHARTYPE* text, int text_len)\n{\n    stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len);\n    ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW);\n    state->cursor = state->select_start = state->select_end = 0;\n    if (text_len <= 0)\n        return;\n    if (ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len))\n    {\n        state->cursor = state->select_start = state->select_end = text_len;\n        state->has_preferred_x = 0;\n        return;\n    }\n    IM_ASSERT(0); // Failed to insert character, normally shouldn't happen because of how we currently use stb_textedit_replace()\n}\n\n} // namespace ImStb\n\nvoid ImGuiInputTextState::OnKeyPressed(int key)\n{\n    stb_textedit_key(this, &Stb, key);\n    CursorFollow = true;\n    CursorAnimReset();\n}\n\nImGuiInputTextCallbackData::ImGuiInputTextCallbackData()\n{\n    memset(this, 0, sizeof(*this));\n}\n\n// Public API to manipulate UTF-8 text\n// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar)\n// FIXME: The existence of this rarely exercised code path is a bit of a nuisance.\nvoid ImGuiInputTextCallbackData::DeleteChars(int pos, int bytes_count)\n{\n    IM_ASSERT(pos + bytes_count <= BufTextLen);\n    char* dst = Buf + pos;\n    const char* src = Buf + pos + bytes_count;\n    while (char c = *src++)\n        *dst++ = c;\n    *dst = '\\0';\n\n    if (CursorPos >= pos + bytes_count)\n        CursorPos -= bytes_count;\n    else if (CursorPos >= pos)\n        CursorPos = pos;\n    SelectionStart = SelectionEnd = CursorPos;\n    BufDirty = true;\n    BufTextLen -= bytes_count;\n}\n\nvoid ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end)\n{\n    // Accept null ranges\n    if (new_text == new_text_end)\n        return;\n\n    const bool is_resizable = (Flags & ImGuiInputTextFlags_CallbackResize) != 0;\n    const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text);\n    if (new_text_len + BufTextLen >= BufSize)\n    {\n        if (!is_resizable)\n            return;\n\n        // Contrary to STB_TEXTEDIT_INSERTCHARS() this is working in the UTF8 buffer, hence the mildly similar code (until we remove the U16 buffer altogether!)\n        ImGuiContext& g = *Ctx;\n        ImGuiInputTextState* edit_state = &g.InputTextState;\n        IM_ASSERT(edit_state->ID != 0 && g.ActiveId == edit_state->ID);\n        IM_ASSERT(Buf == edit_state->TextA.Data);\n        int new_buf_size = BufTextLen + ImClamp(new_text_len * 4, 32, ImMax(256, new_text_len)) + 1;\n        edit_state->TextA.reserve(new_buf_size + 1);\n        Buf = edit_state->TextA.Data;\n        BufSize = edit_state->BufCapacityA = new_buf_size;\n    }\n\n    if (BufTextLen != pos)\n        memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos));\n    memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char));\n    Buf[BufTextLen + new_text_len] = '\\0';\n\n    if (CursorPos >= pos)\n        CursorPos += new_text_len;\n    SelectionStart = SelectionEnd = CursorPos;\n    BufDirty = true;\n    BufTextLen += new_text_len;\n}\n\n// Return false to discard a character.\nstatic bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source)\n{\n    IM_ASSERT(input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Clipboard);\n    unsigned int c = *p_char;\n\n    // Filter non-printable (NB: isprint is unreliable! see #2467)\n    bool apply_named_filters = true;\n    if (c < 0x20)\n    {\n        bool pass = false;\n        pass |= (c == '\\n' && (flags & ImGuiInputTextFlags_Multiline)); // Note that an Enter KEY will emit \\r and be ignored (we poll for KEY in InputText() code)\n        pass |= (c == '\\t' && (flags & ImGuiInputTextFlags_AllowTabInput));\n        if (!pass)\n            return false;\n        apply_named_filters = false; // Override named filters below so newline and tabs can still be inserted.\n    }\n\n    if (input_source != ImGuiInputSource_Clipboard)\n    {\n        // We ignore Ascii representation of delete (emitted from Backspace on OSX, see #2578, #2817)\n        if (c == 127)\n            return false;\n\n        // Filter private Unicode range. GLFW on OSX seems to send private characters for special keys like arrow keys (FIXME)\n        if (c >= 0xE000 && c <= 0xF8FF)\n            return false;\n    }\n\n    // Filter Unicode ranges we are not handling in this build\n    if (c > IM_UNICODE_CODEPOINT_MAX)\n        return false;\n\n    // Generic named filters\n    if (apply_named_filters && (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific)))\n    {\n        // The libc allows overriding locale, with e.g. 'setlocale(LC_NUMERIC, \"de_DE.UTF-8\");' which affect the output/input of printf/scanf to use e.g. ',' instead of '.'.\n        // The standard mandate that programs starts in the \"C\" locale where the decimal point is '.'.\n        // We don't really intend to provide widespread support for it, but out of empathy for people stuck with using odd API, we support the bare minimum aka overriding the decimal point.\n        // Change the default decimal_point with:\n        //   ImGui::GetIO()->PlatformLocaleDecimalPoint = *localeconv()->decimal_point;\n        // Users of non-default decimal point (in particular ',') may be affected by word-selection logic (is_word_boundary_from_right/is_word_boundary_from_left) functions.\n        ImGuiContext& g = *ctx;\n        const unsigned c_decimal_point = (unsigned int)g.IO.PlatformLocaleDecimalPoint;\n        if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific))\n            if (c == '.' || c == ',')\n                c = c_decimal_point;\n\n        // Full-width -> half-width conversion for numeric fields (https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block)\n        // While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may\n        // scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font.\n        if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal))\n            if (c >= 0xFF01 && c <= 0xFF5E)\n                c = c - 0xFF01 + 0x21;\n\n        // Allow 0-9 . - + * /\n        if (flags & ImGuiInputTextFlags_CharsDecimal)\n            if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/'))\n                return false;\n\n        // Allow 0-9 . - + * / e E\n        if (flags & ImGuiInputTextFlags_CharsScientific)\n            if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E'))\n                return false;\n\n        // Allow 0-9 a-F A-F\n        if (flags & ImGuiInputTextFlags_CharsHexadecimal)\n            if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F'))\n                return false;\n\n        // Turn a-z into A-Z\n        if (flags & ImGuiInputTextFlags_CharsUppercase)\n            if (c >= 'a' && c <= 'z')\n                c += (unsigned int)('A' - 'a');\n\n        if (flags & ImGuiInputTextFlags_CharsNoBlank)\n            if (ImCharIsBlankW(c))\n                return false;\n\n        *p_char = c;\n    }\n\n    // Custom callback filter\n    if (flags & ImGuiInputTextFlags_CallbackCharFilter)\n    {\n        ImGuiContext& g = *GImGui;\n        ImGuiInputTextCallbackData callback_data;\n        callback_data.Ctx = &g;\n        callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter;\n        callback_data.EventChar = (ImWchar)c;\n        callback_data.Flags = flags;\n        callback_data.UserData = user_data;\n        if (callback(&callback_data) != 0)\n            return false;\n        *p_char = callback_data.EventChar;\n        if (!callback_data.EventChar)\n            return false;\n    }\n\n    return true;\n}\n\n// Find the shortest single replacement we can make to get the new text from the old text.\n// Important: needs to be run before TextW is rewritten with the new characters because calling STB_TEXTEDIT_GETCHAR() at the end.\n// FIXME: Ideally we should transition toward (1) making InsertChars()/DeleteChars() update undo-stack (2) discourage (and keep reconcile) or obsolete (and remove reconcile) accessing buffer directly.\nstatic void InputTextReconcileUndoStateAfterUserCallback(ImGuiInputTextState* state, const char* new_buf_a, int new_length_a)\n{\n    ImGuiContext& g = *GImGui;\n    const ImWchar* old_buf = state->TextW.Data;\n    const int old_length = state->CurLenW;\n    const int new_length = ImTextCountCharsFromUtf8(new_buf_a, new_buf_a + new_length_a);\n    g.TempBuffer.reserve_discard((new_length + 1) * sizeof(ImWchar));\n    ImWchar* new_buf = (ImWchar*)(void*)g.TempBuffer.Data;\n    ImTextStrFromUtf8(new_buf, new_length + 1, new_buf_a, new_buf_a + new_length_a);\n\n    const int shorter_length = ImMin(old_length, new_length);\n    int first_diff;\n    for (first_diff = 0; first_diff < shorter_length; first_diff++)\n        if (old_buf[first_diff] != new_buf[first_diff])\n            break;\n    if (first_diff == old_length && first_diff == new_length)\n        return;\n\n    int old_last_diff = old_length - 1;\n    int new_last_diff = new_length - 1;\n    for (; old_last_diff >= first_diff && new_last_diff >= first_diff; old_last_diff--, new_last_diff--)\n        if (old_buf[old_last_diff] != new_buf[new_last_diff])\n            break;\n\n    const int insert_len = new_last_diff - first_diff + 1;\n    const int delete_len = old_last_diff - first_diff + 1;\n    if (insert_len > 0 || delete_len > 0)\n        if (STB_TEXTEDIT_CHARTYPE* p = stb_text_createundo(&state->Stb.undostate, first_diff, delete_len, insert_len))\n            for (int i = 0; i < delete_len; i++)\n                p[i] = ImStb::STB_TEXTEDIT_GETCHAR(state, first_diff + i);\n}\n\n// As InputText() retain textual data and we currently provide a path for user to not retain it (via local variables)\n// we need some form of hook to reapply data back to user buffer on deactivation frame. (#4714)\n// It would be more desirable that we discourage users from taking advantage of the \"user not retaining data\" trick,\n// but that more likely be attractive when we do have _NoLiveEdit flag available.\nvoid ImGui::InputTextDeactivateHook(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiInputTextState* state = &g.InputTextState;\n    if (id == 0 || state->ID != id)\n        return;\n    g.InputTextDeactivatedState.ID = state->ID;\n    if (state->Flags & ImGuiInputTextFlags_ReadOnly)\n    {\n        g.InputTextDeactivatedState.TextA.resize(0); // In theory this data won't be used, but clear to be neat.\n    }\n    else\n    {\n        IM_ASSERT(state->TextA.Data != 0);\n        g.InputTextDeactivatedState.TextA.resize(state->CurLenA + 1);\n        memcpy(g.InputTextDeactivatedState.TextA.Data, state->TextA.Data, state->CurLenA + 1);\n    }\n}\n\n// Edit a string of text\n// - buf_size account for the zero-terminator, so a buf_size of 6 can hold \"Hello\" but not \"Hello!\".\n//   This is so we can easily call InputText() on static arrays using ARRAYSIZE() and to match\n//   Note that in std::string world, capacity() would omit 1 byte used by the zero-terminator.\n// - When active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while the InputText is active has no effect.\n// - If you want to use ImGui::InputText() with std::string, see misc/cpp/imgui_stdlib.h\n// (FIXME: Rather confusing and messy function, among the worse part of our codebase, expecting to rewrite a V2 at some point.. Partly because we are\n//  doing UTF8 > U16 > UTF8 conversions on the go to easily interface with stb_textedit. Ideally should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188)\nbool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* callback_user_data)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    IM_ASSERT(buf != NULL && buf_size >= 0);\n    IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline)));        // Can't use both together (they both use up/down keys)\n    IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key)\n\n    ImGuiContext& g = *GImGui;\n    ImGuiIO& io = g.IO;\n    const ImGuiStyle& style = g.Style;\n\n    const bool RENDER_SELECTION_WHEN_INACTIVE = false;\n    const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0;\n    const bool is_readonly = (flags & ImGuiInputTextFlags_ReadOnly) != 0;\n    const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0;\n    const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0;\n    const bool is_resizable = (flags & ImGuiInputTextFlags_CallbackResize) != 0;\n    if (is_resizable)\n        IM_ASSERT(callback != NULL); // Must provide a callback if you set the ImGuiInputTextFlags_CallbackResize flag!\n\n    if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope (including the scrollbar)\n        BeginGroup();\n    const ImGuiID id = window->GetID(label);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? g.FontSize * 8.0f : label_size.y) + style.FramePadding.y * 2.0f); // Arbitrary default of 8 lines high for multi-line\n    const ImVec2 total_size = ImVec2(frame_size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), frame_size.y);\n\n    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);\n    const ImRect total_bb(frame_bb.Min, frame_bb.Min + total_size);\n\n    ImGuiWindow* draw_window = window;\n    ImVec2 inner_size = frame_size;\n    ImGuiItemStatusFlags item_status_flags = 0;\n    ImGuiLastItemData item_data_backup;\n    if (is_multiline)\n    {\n        ImVec2 backup_pos = window->DC.CursorPos;\n        ItemSize(total_bb, style.FramePadding.y);\n        if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable))\n        {\n            EndGroup();\n            return false;\n        }\n        item_status_flags = g.LastItemData.StatusFlags;\n        item_data_backup = g.LastItemData;\n        window->DC.CursorPos = backup_pos;\n\n        // Prevent NavActivate reactivating in BeginChild().\n        const ImGuiID backup_activate_id = g.NavActivateId;\n        if (g.ActiveId == id) // Prevent reactivation\n            g.NavActivateId = 0;\n\n        // We reproduce the contents of BeginChildFrame() in order to provide 'label' so our window internal data are easier to read/debug.\n        PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]);\n        PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding);\n        PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize);\n        PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // Ensure no clip rect so mouse hover can reach FramePadding edges\n        bool child_visible = BeginChildEx(label, id, frame_bb.GetSize(), true, ImGuiWindowFlags_NoMove);\n        g.NavActivateId = backup_activate_id;\n        PopStyleVar(3);\n        PopStyleColor();\n        if (!child_visible)\n        {\n            EndChild();\n            EndGroup();\n            return false;\n        }\n        draw_window = g.CurrentWindow; // Child window\n        draw_window->DC.NavLayersActiveMaskNext |= (1 << draw_window->DC.NavLayerCurrent); // This is to ensure that EndChild() will display a navigation highlight so we can \"enter\" into it.\n        draw_window->DC.CursorPos += style.FramePadding;\n        inner_size.x -= draw_window->ScrollbarSizes.x;\n    }\n    else\n    {\n        // Support for internal ImGuiInputTextFlags_MergedItem flag, which could be redesigned as an ItemFlags if needed (with test performed in ItemAdd)\n        ItemSize(total_bb, style.FramePadding.y);\n        if (!(flags & ImGuiInputTextFlags_MergedItem))\n            if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable))\n                return false;\n        item_status_flags = g.LastItemData.StatusFlags;\n    }\n    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);\n    if (hovered)\n        g.MouseCursor = ImGuiMouseCursor_TextInput;\n\n    // We are only allowed to access the state if we are already the active widget.\n    ImGuiInputTextState* state = GetInputTextState(id);\n\n    const bool input_requested_by_tabbing = (item_status_flags & ImGuiItemStatusFlags_FocusedByTabbing) != 0;\n    const bool input_requested_by_nav = (g.ActiveId != id) && ((g.NavActivateId == id) && ((g.NavActivateFlags & ImGuiActivateFlags_PreferInput) || (g.NavInputSource == ImGuiInputSource_Keyboard)));\n\n    const bool user_clicked = hovered && io.MouseClicked[0];\n    const bool user_scroll_finish = is_multiline && state != NULL && g.ActiveId == 0 && g.ActiveIdPreviousFrame == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);\n    const bool user_scroll_active = is_multiline && state != NULL && g.ActiveId == GetWindowScrollbarID(draw_window, ImGuiAxis_Y);\n    bool clear_active_id = false;\n    bool select_all = false;\n\n    float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX;\n\n    const bool init_changed_specs = (state != NULL && state->Stb.single_line != !is_multiline); // state != NULL means its our state.\n    const bool init_make_active = (user_clicked || user_scroll_finish || input_requested_by_nav || input_requested_by_tabbing);\n    const bool init_state = (init_make_active || user_scroll_active);\n    if ((init_state && g.ActiveId != id) || init_changed_specs)\n    {\n        // Access state even if we don't own it yet.\n        state = &g.InputTextState;\n        state->CursorAnimReset();\n\n        // Backup state of deactivating item so they'll have a chance to do a write to output buffer on the same frame they report IsItemDeactivatedAfterEdit (#4714)\n        InputTextDeactivateHook(state->ID);\n\n        // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar)\n        // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode)\n        const int buf_len = (int)strlen(buf);\n        state->InitialTextA.resize(buf_len + 1);    // UTF-8. we use +1 to make sure that .Data is always pointing to at least an empty string.\n        memcpy(state->InitialTextA.Data, buf, buf_len + 1);\n\n        // Preserve cursor position and undo/redo stack if we come back to same widget\n        // FIXME: Since we reworked this on 2022/06, may want to differenciate recycle_cursor vs recycle_undostate?\n        bool recycle_state = (state->ID == id && !init_changed_specs);\n        if (recycle_state && (state->CurLenA != buf_len || (state->TextAIsValid && strncmp(state->TextA.Data, buf, buf_len) != 0)))\n            recycle_state = false;\n\n        // Start edition\n        const char* buf_end = NULL;\n        state->ID = id;\n        state->TextW.resize(buf_size + 1);          // wchar count <= UTF-8 count. we use +1 to make sure that .Data is always pointing to at least an empty string.\n        state->TextA.resize(0);\n        state->TextAIsValid = false;                // TextA is not valid yet (we will display buf until then)\n        state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, buf_size, buf, NULL, &buf_end);\n        state->CurLenA = (int)(buf_end - buf);      // We can't get the result from ImStrncpy() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8.\n\n        if (recycle_state)\n        {\n            // Recycle existing cursor/selection/undo stack but clamp position\n            // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler.\n            state->CursorClamp();\n        }\n        else\n        {\n            state->ScrollX = 0.0f;\n            stb_textedit_initialize_state(&state->Stb, !is_multiline);\n        }\n\n        if (!is_multiline)\n        {\n            if (flags & ImGuiInputTextFlags_AutoSelectAll)\n                select_all = true;\n            if (input_requested_by_nav && (!recycle_state || !(g.NavActivateFlags & ImGuiActivateFlags_TryToPreserveState)))\n                select_all = true;\n            if (input_requested_by_tabbing || (user_clicked && io.KeyCtrl))\n                select_all = true;\n        }\n\n        if (flags & ImGuiInputTextFlags_AlwaysOverwrite)\n            state->Stb.insert_mode = 1; // stb field name is indeed incorrect (see #2863)\n    }\n\n    const bool is_osx = io.ConfigMacOSXBehaviors;\n    if (g.ActiveId != id && init_make_active)\n    {\n        IM_ASSERT(state && state->ID == id);\n        SetActiveID(id, window);\n        SetFocusID(id, window);\n        FocusWindow(window);\n    }\n    if (g.ActiveId == id)\n    {\n        // Declare some inputs, the other are registered and polled via Shortcut() routing system.\n        if (user_clicked)\n            SetKeyOwner(ImGuiKey_MouseLeft, id);\n        g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right);\n        if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory))\n            g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down);\n        SetKeyOwner(ImGuiKey_Home, id);\n        SetKeyOwner(ImGuiKey_End, id);\n        if (is_multiline)\n        {\n            SetKeyOwner(ImGuiKey_PageUp, id);\n            SetKeyOwner(ImGuiKey_PageDown, id);\n        }\n        if (is_osx)\n            SetKeyOwner(ImGuiMod_Alt, id);\n        if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \\t character.\n            SetShortcutRouting(ImGuiKey_Tab, id);\n    }\n\n    // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function)\n    if (g.ActiveId == id && state == NULL)\n        ClearActiveID();\n\n    // Release focus when we click outside\n    if (g.ActiveId == id && io.MouseClicked[0] && !init_state && !init_make_active) //-V560\n        clear_active_id = true;\n\n    // Lock the decision of whether we are going to take the path displaying the cursor or selection\n    bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active);\n    bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor);\n    bool value_changed = false;\n    bool validated = false;\n\n    // When read-only we always use the live data passed to the function\n    // FIXME-OPT: Because our selection/cursor code currently needs the wide text we need to convert it when active, which is not ideal :(\n    if (is_readonly && state != NULL && (render_cursor || render_selection))\n    {\n        const char* buf_end = NULL;\n        state->TextW.resize(buf_size + 1);\n        state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, buf, NULL, &buf_end);\n        state->CurLenA = (int)(buf_end - buf);\n        state->CursorClamp();\n        render_selection &= state->HasSelection();\n    }\n\n    // Select the buffer to render.\n    const bool buf_display_from_state = (render_cursor || render_selection || g.ActiveId == id) && !is_readonly && state && state->TextAIsValid;\n    const bool is_displaying_hint = (hint != NULL && (buf_display_from_state ? state->TextA.Data : buf)[0] == 0);\n\n    // Password pushes a temporary font with only a fallback glyph\n    if (is_password && !is_displaying_hint)\n    {\n        const ImFontGlyph* glyph = g.Font->FindGlyph('*');\n        ImFont* password_font = &g.InputTextPasswordFont;\n        password_font->FontSize = g.Font->FontSize;\n        password_font->Scale = g.Font->Scale;\n        password_font->Ascent = g.Font->Ascent;\n        password_font->Descent = g.Font->Descent;\n        password_font->ContainerAtlas = g.Font->ContainerAtlas;\n        password_font->FallbackGlyph = glyph;\n        password_font->FallbackAdvanceX = glyph->AdvanceX;\n        IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty());\n        PushFont(password_font);\n    }\n\n    // Process mouse inputs and character inputs\n    int backup_current_text_length = 0;\n    if (g.ActiveId == id)\n    {\n        IM_ASSERT(state != NULL);\n        backup_current_text_length = state->CurLenA;\n        state->Edited = false;\n        state->BufCapacityA = buf_size;\n        state->Flags = flags;\n\n        // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget.\n        // Down the line we should have a cleaner library-wide concept of Selected vs Active.\n        g.ActiveIdAllowOverlap = !io.MouseDown[0];\n\n        // Edit in progress\n        const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + state->ScrollX;\n        const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y) : (g.FontSize * 0.5f));\n\n        if (select_all)\n        {\n            state->SelectAll();\n            state->SelectedAllMouseLock = true;\n        }\n        else if (hovered && io.MouseClickedCount[0] >= 2 && !io.KeyShift)\n        {\n            stb_textedit_click(state, &state->Stb, mouse_x, mouse_y);\n            const int multiclick_count = (io.MouseClickedCount[0] - 2);\n            if ((multiclick_count % 2) == 0)\n            {\n                // Double-click: Select word\n                // We always use the \"Mac\" word advance for double-click select vs CTRL+Right which use the platform dependent variant:\n                // FIXME: There are likely many ways to improve this behavior, but there's no \"right\" behavior (depends on use-case, software, OS)\n                const bool is_bol = (state->Stb.cursor == 0) || ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor - 1) == '\\n';\n                if (STB_TEXT_HAS_SELECTION(&state->Stb) || !is_bol)\n                    state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT);\n                //state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);\n                if (!STB_TEXT_HAS_SELECTION(&state->Stb))\n                    ImStb::stb_textedit_prep_selection_at_cursor(&state->Stb);\n                state->Stb.cursor = ImStb::STB_TEXTEDIT_MOVEWORDRIGHT_MAC(state, state->Stb.cursor);\n                state->Stb.select_end = state->Stb.cursor;\n                ImStb::stb_textedit_clamp(state, &state->Stb);\n            }\n            else\n            {\n                // Triple-click: Select line\n                const bool is_eol = ImStb::STB_TEXTEDIT_GETCHAR(state, state->Stb.cursor) == '\\n';\n                state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART);\n                state->OnKeyPressed(STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT);\n                state->OnKeyPressed(STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT);\n                if (!is_eol && is_multiline)\n                {\n                    ImSwap(state->Stb.select_start, state->Stb.select_end);\n                    state->Stb.cursor = state->Stb.select_end;\n                }\n                state->CursorFollow = false;\n            }\n            state->CursorAnimReset();\n        }\n        else if (io.MouseClicked[0] && !state->SelectedAllMouseLock)\n        {\n            if (hovered)\n            {\n                if (io.KeyShift)\n                    stb_textedit_drag(state, &state->Stb, mouse_x, mouse_y);\n                else\n                    stb_textedit_click(state, &state->Stb, mouse_x, mouse_y);\n                state->CursorAnimReset();\n            }\n        }\n        else if (io.MouseDown[0] && !state->SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f))\n        {\n            stb_textedit_drag(state, &state->Stb, mouse_x, mouse_y);\n            state->CursorAnimReset();\n            state->CursorFollow = true;\n        }\n        if (state->SelectedAllMouseLock && !io.MouseDown[0])\n            state->SelectedAllMouseLock = false;\n\n        // We expect backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336)\n        // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes)\n        if ((flags & ImGuiInputTextFlags_AllowTabInput) && Shortcut(ImGuiKey_Tab, id) && !is_readonly)\n        {\n            unsigned int c = '\\t'; // Insert TAB\n            if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))\n                state->OnKeyPressed((int)c);\n        }\n\n        // Process regular text input (before we check for Return because using some IME will effectively send a Return?)\n        // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters.\n        const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper);\n        if (io.InputQueueCharacters.Size > 0)\n        {\n            if (!ignore_char_inputs && !is_readonly && !input_requested_by_nav)\n                for (int n = 0; n < io.InputQueueCharacters.Size; n++)\n                {\n                    // Insert character if they pass filtering\n                    unsigned int c = (unsigned int)io.InputQueueCharacters[n];\n                    if (c == '\\t') // Skip Tab, see above.\n                        continue;\n                    if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))\n                        state->OnKeyPressed((int)c);\n                }\n\n            // Consume characters\n            io.InputQueueCharacters.resize(0);\n        }\n    }\n\n    // Process other shortcuts/key-presses\n    bool revert_edit = false;\n    if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id)\n    {\n        IM_ASSERT(state != NULL);\n\n        const int row_count_per_page = ImMax((int)((inner_size.y - style.FramePadding.y) / g.FontSize), 1);\n        state->Stb.row_count_per_page = row_count_per_page;\n\n        const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0);\n        const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl;                     // OS X style: Text editing cursor movement using Alt instead of Ctrl\n        const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt;  // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End\n\n        // Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: formet would be handled by InputText)\n        // Otherwise we could simply assume that we own the keys as we are active.\n        const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat;\n        const bool is_cut   = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_X, id, f_repeat) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, id, f_repeat)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection());\n        const bool is_copy  = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_C, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, id))  && !is_password && (!is_multiline || state->HasSelection());\n        const bool is_paste = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_V, id, f_repeat) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, id, f_repeat)) && !is_readonly;\n        const bool is_undo  = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Z, id, f_repeat)) && !is_readonly && is_undoable;\n        const bool is_redo =  (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Y, id, f_repeat) || (is_osx && Shortcut(ImGuiMod_Shortcut | ImGuiMod_Shift | ImGuiKey_Z, id, f_repeat))) && !is_readonly && is_undoable;\n        const bool is_select_all = Shortcut(ImGuiMod_Shortcut | ImGuiKey_A, id);\n\n        // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful.\n        const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0;\n        const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true);\n        const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false));\n        const bool is_cancel = Shortcut(ImGuiKey_Escape, id, f_repeat) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, id, f_repeat));\n\n        // FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of.\n        if (IsKeyPressed(ImGuiKey_LeftArrow))                        { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_RightArrow))                  { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline)     { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_DownArrow) && is_multiline)   { if (io.KeyCtrl) SetScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_PageUp) && is_multiline)      { state->OnKeyPressed(STB_TEXTEDIT_K_PGUP | k_mask); scroll_y -= row_count_per_page * g.FontSize; }\n        else if (IsKeyPressed(ImGuiKey_PageDown) && is_multiline)    { state->OnKeyPressed(STB_TEXTEDIT_K_PGDOWN | k_mask); scroll_y += row_count_per_page * g.FontSize; }\n        else if (IsKeyPressed(ImGuiKey_Home))                        { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_End))                         { state->OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); }\n        else if (IsKeyPressed(ImGuiKey_Delete) && !is_readonly && !is_cut)\n        {\n            if (!state->HasSelection())\n            {\n                // OSX doesn't seem to have Super+Delete to delete until end-of-line, so we don't emulate that (as opposed to Super+Backspace)\n                if (is_wordmove_key_down)\n                    state->OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT);\n            }\n            state->OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask);\n        }\n        else if (IsKeyPressed(ImGuiKey_Backspace) && !is_readonly)\n        {\n            if (!state->HasSelection())\n            {\n                if (is_wordmove_key_down)\n                    state->OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT);\n                else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl)\n                    state->OnKeyPressed(STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT);\n            }\n            state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask);\n        }\n        else if (is_enter_pressed || is_gamepad_validate)\n        {\n            // Determine if we turn Enter into a \\n character\n            bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0;\n            if (!is_multiline || is_gamepad_validate || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl))\n            {\n                validated = true;\n                if (io.ConfigInputTextEnterKeepActive && !is_multiline)\n                    state->SelectAll(); // No need to scroll\n                else\n                    clear_active_id = true;\n            }\n            else if (!is_readonly)\n            {\n                unsigned int c = '\\n'; // Insert new line\n                if (InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Keyboard))\n                    state->OnKeyPressed((int)c);\n            }\n        }\n        else if (is_cancel)\n        {\n            if (flags & ImGuiInputTextFlags_EscapeClearsAll)\n            {\n                if (buf[0] != 0)\n                {\n                    revert_edit = true;\n                }\n                else\n                {\n                    render_cursor = render_selection = false;\n                    clear_active_id = true;\n                }\n            }\n            else\n            {\n                clear_active_id = revert_edit = true;\n                render_cursor = render_selection = false;\n            }\n        }\n        else if (is_undo || is_redo)\n        {\n            state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO);\n            state->ClearSelection();\n        }\n        else if (is_select_all)\n        {\n            state->SelectAll();\n            state->CursorFollow = true;\n        }\n        else if (is_cut || is_copy)\n        {\n            // Cut, Copy\n            if (io.SetClipboardTextFn)\n            {\n                const int ib = state->HasSelection() ? ImMin(state->Stb.select_start, state->Stb.select_end) : 0;\n                const int ie = state->HasSelection() ? ImMax(state->Stb.select_start, state->Stb.select_end) : state->CurLenW;\n                const int clipboard_data_len = ImTextCountUtf8BytesFromStr(state->TextW.Data + ib, state->TextW.Data + ie) + 1;\n                char* clipboard_data = (char*)IM_ALLOC(clipboard_data_len * sizeof(char));\n                ImTextStrToUtf8(clipboard_data, clipboard_data_len, state->TextW.Data + ib, state->TextW.Data + ie);\n                SetClipboardText(clipboard_data);\n                MemFree(clipboard_data);\n            }\n            if (is_cut)\n            {\n                if (!state->HasSelection())\n                    state->SelectAll();\n                state->CursorFollow = true;\n                stb_textedit_cut(state, &state->Stb);\n            }\n        }\n        else if (is_paste)\n        {\n            if (const char* clipboard = GetClipboardText())\n            {\n                // Filter pasted buffer\n                const int clipboard_len = (int)strlen(clipboard);\n                ImWchar* clipboard_filtered = (ImWchar*)IM_ALLOC((clipboard_len + 1) * sizeof(ImWchar));\n                int clipboard_filtered_len = 0;\n                for (const char* s = clipboard; *s != 0; )\n                {\n                    unsigned int c;\n                    s += ImTextCharFromUtf8(&c, s, NULL);\n                    if (!InputTextFilterCharacter(&g, &c, flags, callback, callback_user_data, ImGuiInputSource_Clipboard))\n                        continue;\n                    clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c;\n                }\n                clipboard_filtered[clipboard_filtered_len] = 0;\n                if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation\n                {\n                    stb_textedit_paste(state, &state->Stb, clipboard_filtered, clipboard_filtered_len);\n                    state->CursorFollow = true;\n                }\n                MemFree(clipboard_filtered);\n            }\n        }\n\n        // Update render selection flag after events have been handled, so selection highlight can be displayed during the same frame.\n        render_selection |= state->HasSelection() && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor);\n    }\n\n    // Process callbacks and apply result back to user's buffer.\n    const char* apply_new_text = NULL;\n    int apply_new_text_length = 0;\n    if (g.ActiveId == id)\n    {\n        IM_ASSERT(state != NULL);\n        if (revert_edit && !is_readonly)\n        {\n            if (flags & ImGuiInputTextFlags_EscapeClearsAll)\n            {\n                // Clear input\n                IM_ASSERT(buf[0] != 0);\n                apply_new_text = \"\";\n                apply_new_text_length = 0;\n                value_changed = true;\n                STB_TEXTEDIT_CHARTYPE empty_string;\n                stb_textedit_replace(state, &state->Stb, &empty_string, 0);\n            }\n            else if (strcmp(buf, state->InitialTextA.Data) != 0)\n            {\n                // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents.\n                // Push records into the undo stack so we can CTRL+Z the revert operation itself\n                apply_new_text = state->InitialTextA.Data;\n                apply_new_text_length = state->InitialTextA.Size - 1;\n                value_changed = true;\n                ImVector<ImWchar> w_text;\n                if (apply_new_text_length > 0)\n                {\n                    w_text.resize(ImTextCountCharsFromUtf8(apply_new_text, apply_new_text + apply_new_text_length) + 1);\n                    ImTextStrFromUtf8(w_text.Data, w_text.Size, apply_new_text, apply_new_text + apply_new_text_length);\n                }\n                stb_textedit_replace(state, &state->Stb, w_text.Data, (apply_new_text_length > 0) ? (w_text.Size - 1) : 0);\n            }\n        }\n\n        // Apply ASCII value\n        if (!is_readonly)\n        {\n            state->TextAIsValid = true;\n            state->TextA.resize(state->TextW.Size * 4 + 1);\n            ImTextStrToUtf8(state->TextA.Data, state->TextA.Size, state->TextW.Data, NULL);\n        }\n\n        // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer\n        // before clearing ActiveId, even though strictly speaking it wasn't modified on this frame.\n        // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail.\n        // This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage\n        // (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object\n        // unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize).\n        const bool apply_edit_back_to_user_buffer = !revert_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0);\n        if (apply_edit_back_to_user_buffer)\n        {\n            // Apply new value immediately - copy modified buffer back\n            // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer\n            // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect.\n            // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks.\n\n            // User callback\n            if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CallbackAlways)) != 0)\n            {\n                IM_ASSERT(callback != NULL);\n\n                // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment.\n                ImGuiInputTextFlags event_flag = 0;\n                ImGuiKey event_key = ImGuiKey_None;\n                if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && Shortcut(ImGuiKey_Tab, id))\n                {\n                    event_flag = ImGuiInputTextFlags_CallbackCompletion;\n                    event_key = ImGuiKey_Tab;\n                }\n                else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_UpArrow))\n                {\n                    event_flag = ImGuiInputTextFlags_CallbackHistory;\n                    event_key = ImGuiKey_UpArrow;\n                }\n                else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressed(ImGuiKey_DownArrow))\n                {\n                    event_flag = ImGuiInputTextFlags_CallbackHistory;\n                    event_key = ImGuiKey_DownArrow;\n                }\n                else if ((flags & ImGuiInputTextFlags_CallbackEdit) && state->Edited)\n                {\n                    event_flag = ImGuiInputTextFlags_CallbackEdit;\n                }\n                else if (flags & ImGuiInputTextFlags_CallbackAlways)\n                {\n                    event_flag = ImGuiInputTextFlags_CallbackAlways;\n                }\n\n                if (event_flag)\n                {\n                    ImGuiInputTextCallbackData callback_data;\n                    callback_data.Ctx = &g;\n                    callback_data.EventFlag = event_flag;\n                    callback_data.Flags = flags;\n                    callback_data.UserData = callback_user_data;\n\n                    char* callback_buf = is_readonly ? buf : state->TextA.Data;\n                    callback_data.EventKey = event_key;\n                    callback_data.Buf = callback_buf;\n                    callback_data.BufTextLen = state->CurLenA;\n                    callback_data.BufSize = state->BufCapacityA;\n                    callback_data.BufDirty = false;\n\n                    // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188)\n                    ImWchar* text = state->TextW.Data;\n                    const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + state->Stb.cursor);\n                    const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_start);\n                    const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + state->Stb.select_end);\n\n                    // Call user code\n                    callback(&callback_data);\n\n                    // Read back what user may have modified\n                    callback_buf = is_readonly ? buf : state->TextA.Data; // Pointer may have been invalidated by a resize callback\n                    IM_ASSERT(callback_data.Buf == callback_buf);         // Invalid to modify those fields\n                    IM_ASSERT(callback_data.BufSize == state->BufCapacityA);\n                    IM_ASSERT(callback_data.Flags == flags);\n                    const bool buf_dirty = callback_data.BufDirty;\n                    if (callback_data.CursorPos != utf8_cursor_pos || buf_dirty)            { state->Stb.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); state->CursorFollow = true; }\n                    if (callback_data.SelectionStart != utf8_selection_start || buf_dirty)  { state->Stb.select_start = (callback_data.SelectionStart == callback_data.CursorPos) ? state->Stb.cursor : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); }\n                    if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty)      { state->Stb.select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb.select_start : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); }\n                    if (buf_dirty)\n                    {\n                        IM_ASSERT((flags & ImGuiInputTextFlags_ReadOnly) == 0);\n                        IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text!\n                        InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ?\n                        if (callback_data.BufTextLen > backup_current_text_length && is_resizable)\n                            state->TextW.resize(state->TextW.Size + (callback_data.BufTextLen - backup_current_text_length)); // Worse case scenario resize\n                        state->CurLenW = ImTextStrFromUtf8(state->TextW.Data, state->TextW.Size, callback_data.Buf, NULL);\n                        state->CurLenA = callback_data.BufTextLen;  // Assume correct length and valid UTF-8 from user, saves us an extra strlen()\n                        state->CursorAnimReset();\n                    }\n                }\n            }\n\n            // Will copy result string if modified\n            if (!is_readonly && strcmp(state->TextA.Data, buf) != 0)\n            {\n                apply_new_text = state->TextA.Data;\n                apply_new_text_length = state->CurLenA;\n                value_changed = true;\n            }\n        }\n    }\n\n    // Handle reapplying final data on deactivation (see InputTextDeactivateHook() for details)\n    if (g.InputTextDeactivatedState.ID == id)\n    {\n        if (g.ActiveId != id && IsItemDeactivatedAfterEdit() && !is_readonly && strcmp(g.InputTextDeactivatedState.TextA.Data, buf) != 0)\n        {\n            apply_new_text = g.InputTextDeactivatedState.TextA.Data;\n            apply_new_text_length = g.InputTextDeactivatedState.TextA.Size - 1;\n            value_changed = true;\n            //IMGUI_DEBUG_LOG(\"InputText(): apply Deactivated data for 0x%08X: \\\"%.*s\\\".\\n\", id, apply_new_text_length, apply_new_text);\n        }\n        g.InputTextDeactivatedState.ID = 0;\n    }\n\n    // Copy result to user buffer. This can currently only happen when (g.ActiveId == id)\n    if (apply_new_text != NULL)\n    {\n        // We cannot test for 'backup_current_text_length != apply_new_text_length' here because we have no guarantee that the size\n        // of our owned buffer matches the size of the string object held by the user, and by design we allow InputText() to be used\n        // without any storage on user's side.\n        IM_ASSERT(apply_new_text_length >= 0);\n        if (is_resizable)\n        {\n            ImGuiInputTextCallbackData callback_data;\n            callback_data.Ctx = &g;\n            callback_data.EventFlag = ImGuiInputTextFlags_CallbackResize;\n            callback_data.Flags = flags;\n            callback_data.Buf = buf;\n            callback_data.BufTextLen = apply_new_text_length;\n            callback_data.BufSize = ImMax(buf_size, apply_new_text_length + 1);\n            callback_data.UserData = callback_user_data;\n            callback(&callback_data);\n            buf = callback_data.Buf;\n            buf_size = callback_data.BufSize;\n            apply_new_text_length = ImMin(callback_data.BufTextLen, buf_size - 1);\n            IM_ASSERT(apply_new_text_length <= buf_size);\n        }\n        //IMGUI_DEBUG_PRINT(\"InputText(\\\"%s\\\"): apply_new_text length %d\\n\", label, apply_new_text_length);\n\n        // If the underlying buffer resize was denied or not carried to the next frame, apply_new_text_length+1 may be >= buf_size.\n        ImStrncpy(buf, apply_new_text, ImMin(apply_new_text_length + 1, buf_size));\n    }\n\n    // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value)\n    // Otherwise request text input ahead for next frame.\n    if (g.ActiveId == id && clear_active_id)\n        ClearActiveID();\n    else if (g.ActiveId == id)\n        g.WantTextInputNextFrame = 1;\n\n    // Render frame\n    if (!is_multiline)\n    {\n        RenderNavHighlight(frame_bb, id);\n        RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);\n    }\n\n    const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + inner_size.x, frame_bb.Min.y + inner_size.y); // Not using frame_bb.Max because we have adjusted size\n    ImVec2 draw_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding;\n    ImVec2 text_size(0.0f, 0.0f);\n\n    // Set upper limit of single-line InputTextEx() at 2 million characters strings. The current pathological worst case is a long line\n    // without any carriage return, which would makes ImFont::RenderText() reserve too many vertices and probably crash. Avoid it altogether.\n    // Note that we only use this limit on single-line InputText(), so a pathologically large line on a InputTextMultiline() would still crash.\n    const int buf_display_max_length = 2 * 1024 * 1024;\n    const char* buf_display = buf_display_from_state ? state->TextA.Data : buf; //-V595\n    const char* buf_display_end = NULL; // We have specialized paths below for setting the length\n    if (is_displaying_hint)\n    {\n        buf_display = hint;\n        buf_display_end = hint + strlen(hint);\n    }\n\n    // Render text. We currently only render selection when the widget is active or while scrolling.\n    // FIXME: We could remove the '&& render_cursor' to keep rendering selection when inactive.\n    if (render_cursor || render_selection)\n    {\n        IM_ASSERT(state != NULL);\n        if (!is_displaying_hint)\n            buf_display_end = buf_display + state->CurLenA;\n\n        // Render text (with cursor and selection)\n        // This is going to be messy. We need to:\n        // - Display the text (this alone can be more easily clipped)\n        // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation)\n        // - Measure text height (for scrollbar)\n        // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort)\n        // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8.\n        const ImWchar* text_begin = state->TextW.Data;\n        ImVec2 cursor_offset, select_start_offset;\n\n        {\n            // Find lines numbers straddling 'cursor' (slot 0) and 'select_start' (slot 1) positions.\n            const ImWchar* searches_input_ptr[2] = { NULL, NULL };\n            int searches_result_line_no[2] = { -1000, -1000 };\n            int searches_remaining = 0;\n            if (render_cursor)\n            {\n                searches_input_ptr[0] = text_begin + state->Stb.cursor;\n                searches_result_line_no[0] = -1;\n                searches_remaining++;\n            }\n            if (render_selection)\n            {\n                searches_input_ptr[1] = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end);\n                searches_result_line_no[1] = -1;\n                searches_remaining++;\n            }\n\n            // Iterate all lines to find our line numbers\n            // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter.\n            searches_remaining += is_multiline ? 1 : 0;\n            int line_count = 0;\n            //for (const ImWchar* s = text_begin; (s = (const ImWchar*)wcschr((const wchar_t*)s, (wchar_t)'\\n')) != NULL; s++)  // FIXME-OPT: Could use this when wchar_t are 16-bit\n            for (const ImWchar* s = text_begin; *s != 0; s++)\n                if (*s == '\\n')\n                {\n                    line_count++;\n                    if (searches_result_line_no[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_no[0] = line_count; if (--searches_remaining <= 0) break; }\n                    if (searches_result_line_no[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_no[1] = line_count; if (--searches_remaining <= 0) break; }\n                }\n            line_count++;\n            if (searches_result_line_no[0] == -1)\n                searches_result_line_no[0] = line_count;\n            if (searches_result_line_no[1] == -1)\n                searches_result_line_no[1] = line_count;\n\n            // Calculate 2d position by finding the beginning of the line and measuring distance\n            cursor_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x;\n            cursor_offset.y = searches_result_line_no[0] * g.FontSize;\n            if (searches_result_line_no[1] >= 0)\n            {\n                select_start_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x;\n                select_start_offset.y = searches_result_line_no[1] * g.FontSize;\n            }\n\n            // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224)\n            if (is_multiline)\n                text_size = ImVec2(inner_size.x, line_count * g.FontSize);\n        }\n\n        // Scroll\n        if (render_cursor && state->CursorFollow)\n        {\n            // Horizontal scroll in chunks of quarter width\n            if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll))\n            {\n                const float scroll_increment_x = inner_size.x * 0.25f;\n                const float visible_width = inner_size.x - style.FramePadding.x;\n                if (cursor_offset.x < state->ScrollX)\n                    state->ScrollX = IM_TRUNC(ImMax(0.0f, cursor_offset.x - scroll_increment_x));\n                else if (cursor_offset.x - visible_width >= state->ScrollX)\n                    state->ScrollX = IM_TRUNC(cursor_offset.x - visible_width + scroll_increment_x);\n            }\n            else\n            {\n                state->ScrollX = 0.0f;\n            }\n\n            // Vertical scroll\n            if (is_multiline)\n            {\n                // Test if cursor is vertically visible\n                if (cursor_offset.y - g.FontSize < scroll_y)\n                    scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize);\n                else if (cursor_offset.y - (inner_size.y - style.FramePadding.y * 2.0f) >= scroll_y)\n                    scroll_y = cursor_offset.y - inner_size.y + style.FramePadding.y * 2.0f;\n                const float scroll_max_y = ImMax((text_size.y + style.FramePadding.y * 2.0f) - inner_size.y, 0.0f);\n                scroll_y = ImClamp(scroll_y, 0.0f, scroll_max_y);\n                draw_pos.y += (draw_window->Scroll.y - scroll_y);   // Manipulate cursor pos immediately avoid a frame of lag\n                draw_window->Scroll.y = scroll_y;\n            }\n\n            state->CursorFollow = false;\n        }\n\n        // Draw selection\n        const ImVec2 draw_scroll = ImVec2(state->ScrollX, 0.0f);\n        if (render_selection)\n        {\n            const ImWchar* text_selected_begin = text_begin + ImMin(state->Stb.select_start, state->Stb.select_end);\n            const ImWchar* text_selected_end = text_begin + ImMax(state->Stb.select_start, state->Stb.select_end);\n\n            ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg, render_cursor ? 1.0f : 0.6f); // FIXME: current code flow mandate that render_cursor is always true here, we are leaving the transparent one for tests.\n            float bg_offy_up = is_multiline ? 0.0f : -1.0f;    // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection.\n            float bg_offy_dn = is_multiline ? 0.0f : 2.0f;\n            ImVec2 rect_pos = draw_pos + select_start_offset - draw_scroll;\n            for (const ImWchar* p = text_selected_begin; p < text_selected_end; )\n            {\n                if (rect_pos.y > clip_rect.w + g.FontSize)\n                    break;\n                if (rect_pos.y < clip_rect.y)\n                {\n                    //p = (const ImWchar*)wmemchr((const wchar_t*)p, '\\n', text_selected_end - p);  // FIXME-OPT: Could use this when wchar_t are 16-bit\n                    //p = p ? p + 1 : text_selected_end;\n                    while (p < text_selected_end)\n                        if (*p++ == '\\n')\n                            break;\n                }\n                else\n                {\n                    ImVec2 rect_size = InputTextCalcTextSizeW(&g, p, text_selected_end, &p, NULL, true);\n                    if (rect_size.x <= 0.0f) rect_size.x = IM_TRUNC(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines\n                    ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn));\n                    rect.ClipWith(clip_rect);\n                    if (rect.Overlaps(clip_rect))\n                        draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color);\n                }\n                rect_pos.x = draw_pos.x - draw_scroll.x;\n                rect_pos.y += g.FontSize;\n            }\n        }\n\n        // We test for 'buf_display_max_length' as a way to avoid some pathological cases (e.g. single-line 1 MB string) which would make ImDrawList crash.\n        if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)\n        {\n            ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);\n            draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos - draw_scroll, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);\n        }\n\n        // Draw blinking cursor\n        if (render_cursor)\n        {\n            state->CursorAnim += io.DeltaTime;\n            bool cursor_is_visible = (!g.IO.ConfigInputTextCursorBlink) || (state->CursorAnim <= 0.0f) || ImFmod(state->CursorAnim, 1.20f) <= 0.80f;\n            ImVec2 cursor_screen_pos = ImTrunc(draw_pos + cursor_offset - draw_scroll);\n            ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y - g.FontSize + 0.5f, cursor_screen_pos.x + 1.0f, cursor_screen_pos.y - 1.5f);\n            if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect))\n                draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text));\n\n            // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.)\n            if (!is_readonly)\n            {\n                g.PlatformImeData.WantVisible = true;\n                g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize);\n                g.PlatformImeData.InputLineHeight = g.FontSize;\n            }\n        }\n    }\n    else\n    {\n        // Render text only (no selection, no cursor)\n        if (is_multiline)\n            text_size = ImVec2(inner_size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_display_end) * g.FontSize); // We don't need width\n        else if (!is_displaying_hint && g.ActiveId == id)\n            buf_display_end = buf_display + state->CurLenA;\n        else if (!is_displaying_hint)\n            buf_display_end = buf_display + strlen(buf_display);\n\n        if (is_multiline || (buf_display_end - buf_display) < buf_display_max_length)\n        {\n            ImU32 col = GetColorU32(is_displaying_hint ? ImGuiCol_TextDisabled : ImGuiCol_Text);\n            draw_window->DrawList->AddText(g.Font, g.FontSize, draw_pos, col, buf_display, buf_display_end, 0.0f, is_multiline ? NULL : &clip_rect);\n        }\n    }\n\n    if (is_password && !is_displaying_hint)\n        PopFont();\n\n    if (is_multiline)\n    {\n        // For focus requests to work on our multiline we need to ensure our child ItemAdd() call specifies the ImGuiItemFlags_Inputable (ref issue #4761)...\n        Dummy(ImVec2(text_size.x, text_size.y + style.FramePadding.y));\n        g.NextItemData.ItemFlags |= ImGuiItemFlags_Inputable | ImGuiItemFlags_NoTabStop;\n        EndChild();\n        item_data_backup.StatusFlags |= (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredWindow);\n\n        // ...and then we need to undo the group overriding last item data, which gets a bit messy as EndGroup() tries to forward scrollbar being active...\n        // FIXME: This quite messy/tricky, should attempt to get rid of the child window.\n        EndGroup();\n        if (g.LastItemData.ID == 0)\n        {\n            g.LastItemData.ID = id;\n            g.LastItemData.InFlags = item_data_backup.InFlags;\n            g.LastItemData.StatusFlags = item_data_backup.StatusFlags;\n        }\n    }\n\n    // Log as text\n    if (g.LogEnabled && (!is_password || is_displaying_hint))\n    {\n        LogSetNextTextDecoration(\"{\", \"}\");\n        LogRenderedText(&draw_pos, buf_display, buf_display_end);\n    }\n\n    if (label_size.x > 0)\n        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label);\n\n    if (value_changed && !(flags & ImGuiInputTextFlags_NoMarkEdited))\n        MarkItemEdited(id);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Inputable);\n    if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0)\n        return validated;\n    else\n        return value_changed;\n}\n\nvoid ImGui::DebugNodeInputTextState(ImGuiInputTextState* state)\n{\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    ImGuiContext& g = *GImGui;\n    ImStb::STB_TexteditState* stb_state = &state->Stb;\n    ImStb::StbUndoState* undo_state = &stb_state->undostate;\n    Text(\"ID: 0x%08X, ActiveID: 0x%08X\", state->ID, g.ActiveId);\n    DebugLocateItemOnHover(state->ID);\n    Text(\"CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d\", state->CurLenW, state->CurLenA, stb_state->cursor, stb_state->select_start, stb_state->select_end);\n    Text(\"has_preferred_x: %d (%.2f)\", stb_state->has_preferred_x, stb_state->preferred_x);\n    Text(\"undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d\", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point);\n    if (BeginChild(\"undopoints\", ImVec2(0.0f, GetTextLineHeight() * 15), ImGuiChildFlags_Border)) // Visualize undo state\n    {\n        PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));\n        for (int n = 0; n < STB_TEXTEDIT_UNDOSTATECOUNT; n++)\n        {\n            ImStb::StbUndoRecord* undo_rec = &undo_state->undo_rec[n];\n            const char undo_rec_type = (n < undo_state->undo_point) ? 'u' : (n >= undo_state->redo_point) ? 'r' : ' ';\n            if (undo_rec_type == ' ')\n                BeginDisabled();\n            char buf[64] = \"\";\n            if (undo_rec_type != ' ' && undo_rec->char_storage != -1)\n                ImTextStrToUtf8(buf, IM_ARRAYSIZE(buf), undo_state->undo_char + undo_rec->char_storage, undo_state->undo_char + undo_rec->char_storage + undo_rec->insert_length);\n            Text(\"%c [%02d] where %03d, insert %03d, delete %03d, char_storage %03d \\\"%s\\\"\",\n                undo_rec_type, n, undo_rec->where, undo_rec->insert_length, undo_rec->delete_length, undo_rec->char_storage, buf);\n            if (undo_rec_type == ' ')\n                EndDisabled();\n        }\n        PopStyleVar();\n    }\n    EndChild();\n#else\n    IM_UNUSED(state);\n#endif\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: ColorEdit, ColorPicker, ColorButton, etc.\n//-------------------------------------------------------------------------\n// - ColorEdit3()\n// - ColorEdit4()\n// - ColorPicker3()\n// - RenderColorRectWithAlphaCheckerboard() [Internal]\n// - ColorPicker4()\n// - ColorButton()\n// - SetColorEditOptions()\n// - ColorTooltip() [Internal]\n// - ColorEditOptionsPopup() [Internal]\n// - ColorPickerOptionsPopup() [Internal]\n//-------------------------------------------------------------------------\n\nbool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags)\n{\n    return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha);\n}\n\nstatic void ColorEditRestoreH(const float* col, float* H)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.ColorEditCurrentID != 0);\n    if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))\n        return;\n    *H = g.ColorEditSavedHue;\n}\n\n// ColorEdit supports RGB and HSV inputs. In case of RGB input resulting color may have undefined hue and/or saturation.\n// Since widget displays both RGB and HSV values we must preserve hue and saturation to prevent these values resetting.\nstatic void ColorEditRestoreHS(const float* col, float* H, float* S, float* V)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(g.ColorEditCurrentID != 0);\n    if (g.ColorEditSavedID != g.ColorEditCurrentID || g.ColorEditSavedColor != ImGui::ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0)))\n        return;\n\n    // When S == 0, H is undefined.\n    // When H == 1 it wraps around to 0.\n    if (*S == 0.0f || (*H == 0.0f && g.ColorEditSavedHue == 1))\n        *H = g.ColorEditSavedHue;\n\n    // When V == 0, S is undefined.\n    if (*V == 0.0f)\n        *S = g.ColorEditSavedSat;\n}\n\n// Edit colors components (each component in 0.0f..1.0f range).\n// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.\n// With typical options: Left-click on color square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item.\nbool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const float square_sz = GetFrameHeight();\n    const float w_full = CalcItemWidth();\n    const float w_button = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x);\n    const float w_inputs = w_full - w_button;\n    const char* label_display_end = FindRenderedTextEnd(label);\n    g.NextItemData.ClearFlags();\n\n    BeginGroup();\n    PushID(label);\n    const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0);\n    if (set_current_color_edit_id)\n        g.ColorEditCurrentID = window->IDStack.back();\n\n    // If we're not showing any slider there's no point in doing any HSV conversions\n    const ImGuiColorEditFlags flags_untouched = flags;\n    if (flags & ImGuiColorEditFlags_NoInputs)\n        flags = (flags & (~ImGuiColorEditFlags_DisplayMask_)) | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_NoOptions;\n\n    // Context menu: display and modify options (before defaults are applied)\n    if (!(flags & ImGuiColorEditFlags_NoOptions))\n        ColorEditOptionsPopup(col, flags);\n\n    // Read stored options\n    if (!(flags & ImGuiColorEditFlags_DisplayMask_))\n        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DisplayMask_);\n    if (!(flags & ImGuiColorEditFlags_DataTypeMask_))\n        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_DataTypeMask_);\n    if (!(flags & ImGuiColorEditFlags_PickerMask_))\n        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_);\n    if (!(flags & ImGuiColorEditFlags_InputMask_))\n        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_InputMask_);\n    flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_));\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_)); // Check that only 1 is selected\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));   // Check that only 1 is selected\n\n    const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0;\n    const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0;\n    const int components = alpha ? 4 : 3;\n\n    // Convert to the formats we need\n    float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f };\n    if ((flags & ImGuiColorEditFlags_InputHSV) && (flags & ImGuiColorEditFlags_DisplayRGB))\n        ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);\n    else if ((flags & ImGuiColorEditFlags_InputRGB) && (flags & ImGuiColorEditFlags_DisplayHSV))\n    {\n        // Hue is lost when converting from grayscale rgb (saturation=0). Restore it.\n        ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);\n        ColorEditRestoreHS(col, &f[0], &f[1], &f[2]);\n    }\n    int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) };\n\n    bool value_changed = false;\n    bool value_changed_as_float = false;\n\n    const ImVec2 pos = window->DC.CursorPos;\n    const float inputs_offset_x = (style.ColorButtonPosition == ImGuiDir_Left) ? w_button : 0.0f;\n    window->DC.CursorPos.x = pos.x + inputs_offset_x;\n\n    if ((flags & (ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)\n    {\n        // RGB/HSV 0..255 Sliders\n        const float w_item_one  = ImMax(1.0f, IM_TRUNC((w_inputs - (style.ItemInnerSpacing.x) * (components - 1)) / (float)components));\n        const float w_item_last = ImMax(1.0f, IM_TRUNC(w_inputs - (w_item_one + style.ItemInnerSpacing.x) * (components - 1)));\n\n        const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? \"M:0.000\" : \"M:000\").x);\n        static const char* ids[4] = { \"##X\", \"##Y\", \"##Z\", \"##W\" };\n        static const char* fmt_table_int[3][4] =\n        {\n            {   \"%3d\",   \"%3d\",   \"%3d\",   \"%3d\" }, // Short display\n            { \"R:%3d\", \"G:%3d\", \"B:%3d\", \"A:%3d\" }, // Long display for RGBA\n            { \"H:%3d\", \"S:%3d\", \"V:%3d\", \"A:%3d\" }  // Long display for HSVA\n        };\n        static const char* fmt_table_float[3][4] =\n        {\n            {   \"%0.3f\",   \"%0.3f\",   \"%0.3f\",   \"%0.3f\" }, // Short display\n            { \"R:%0.3f\", \"G:%0.3f\", \"B:%0.3f\", \"A:%0.3f\" }, // Long display for RGBA\n            { \"H:%0.3f\", \"S:%0.3f\", \"V:%0.3f\", \"A:%0.3f\" }  // Long display for HSVA\n        };\n        const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_DisplayHSV) ? 2 : 1;\n\n        for (int n = 0; n < components; n++)\n        {\n            if (n > 0)\n                SameLine(0, style.ItemInnerSpacing.x);\n            SetNextItemWidth((n + 1 < components) ? w_item_one : w_item_last);\n\n            // FIXME: When ImGuiColorEditFlags_HDR flag is passed HS values snap in weird ways when SV values go below 0.\n            if (flags & ImGuiColorEditFlags_Float)\n            {\n                value_changed |= DragFloat(ids[n], &f[n], 1.0f / 255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]);\n                value_changed_as_float |= value_changed;\n            }\n            else\n            {\n                value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]);\n            }\n            if (!(flags & ImGuiColorEditFlags_NoOptions))\n                OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n        }\n    }\n    else if ((flags & ImGuiColorEditFlags_DisplayHex) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0)\n    {\n        // RGB Hexadecimal Input\n        char buf[64];\n        if (alpha)\n            ImFormatString(buf, IM_ARRAYSIZE(buf), \"#%02X%02X%02X%02X\", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255), ImClamp(i[3], 0, 255));\n        else\n            ImFormatString(buf, IM_ARRAYSIZE(buf), \"#%02X%02X%02X\", ImClamp(i[0], 0, 255), ImClamp(i[1], 0, 255), ImClamp(i[2], 0, 255));\n        SetNextItemWidth(w_inputs);\n        if (InputText(\"##Text\", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase))\n        {\n            value_changed = true;\n            char* p = buf;\n            while (*p == '#' || ImCharIsBlankA(*p))\n                p++;\n            i[0] = i[1] = i[2] = 0;\n            i[3] = 0xFF; // alpha default to 255 is not parsed by scanf (e.g. inputting #FFFFFF omitting alpha)\n            int r;\n            if (alpha)\n                r = sscanf(p, \"%02X%02X%02X%02X\", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned)\n            else\n                r = sscanf(p, \"%02X%02X%02X\", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]);\n            IM_UNUSED(r); // Fixes C6031: Return value ignored: 'sscanf'.\n        }\n        if (!(flags & ImGuiColorEditFlags_NoOptions))\n            OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n    }\n\n    ImGuiWindow* picker_active_window = NULL;\n    if (!(flags & ImGuiColorEditFlags_NoSmallPreview))\n    {\n        const float button_offset_x = ((flags & ImGuiColorEditFlags_NoInputs) || (style.ColorButtonPosition == ImGuiDir_Left)) ? 0.0f : w_inputs + style.ItemInnerSpacing.x;\n        window->DC.CursorPos = ImVec2(pos.x + button_offset_x, pos.y);\n\n        const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f);\n        if (ColorButton(\"##ColorButton\", col_v4, flags))\n        {\n            if (!(flags & ImGuiColorEditFlags_NoPicker))\n            {\n                // Store current color and open a picker\n                g.ColorPickerRef = col_v4;\n                OpenPopup(\"picker\");\n                SetNextWindowPos(g.LastItemData.Rect.GetBL() + ImVec2(0.0f, style.ItemSpacing.y));\n            }\n        }\n        if (!(flags & ImGuiColorEditFlags_NoOptions))\n            OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n\n        if (BeginPopup(\"picker\"))\n        {\n            if (g.CurrentWindow->BeginCount == 1)\n            {\n                picker_active_window = g.CurrentWindow;\n                if (label != label_display_end)\n                {\n                    TextEx(label, label_display_end);\n                    Spacing();\n                }\n                ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar;\n                ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf;\n                SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes?\n                value_changed |= ColorPicker4(\"##picker\", col, picker_flags, &g.ColorPickerRef.x);\n            }\n            EndPopup();\n        }\n    }\n\n    if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel))\n    {\n        // Position not necessarily next to last submitted button (e.g. if style.ColorButtonPosition == ImGuiDir_Left),\n        // but we need to use SameLine() to setup baseline correctly. Might want to refactor SameLine() to simplify this.\n        SameLine(0.0f, style.ItemInnerSpacing.x);\n        window->DC.CursorPos.x = pos.x + ((flags & ImGuiColorEditFlags_NoInputs) ? w_button : w_full + style.ItemInnerSpacing.x);\n        TextEx(label, label_display_end);\n    }\n\n    // Convert back\n    if (value_changed && picker_active_window == NULL)\n    {\n        if (!value_changed_as_float)\n            for (int n = 0; n < 4; n++)\n                f[n] = i[n] / 255.0f;\n        if ((flags & ImGuiColorEditFlags_DisplayHSV) && (flags & ImGuiColorEditFlags_InputRGB))\n        {\n            g.ColorEditSavedHue = f[0];\n            g.ColorEditSavedSat = f[1];\n            ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]);\n            g.ColorEditSavedID = g.ColorEditCurrentID;\n            g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(f[0], f[1], f[2], 0));\n        }\n        if ((flags & ImGuiColorEditFlags_DisplayRGB) && (flags & ImGuiColorEditFlags_InputHSV))\n            ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]);\n\n        col[0] = f[0];\n        col[1] = f[1];\n        col[2] = f[2];\n        if (alpha)\n            col[3] = f[3];\n    }\n\n    if (set_current_color_edit_id)\n        g.ColorEditCurrentID = 0;\n    PopID();\n    EndGroup();\n\n    // Drag and Drop Target\n    // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test.\n    if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget())\n    {\n        bool accepted_drag_drop = false;\n        if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))\n        {\n            memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512 //-V1086\n            value_changed = accepted_drag_drop = true;\n        }\n        if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))\n        {\n            memcpy((float*)col, payload->Data, sizeof(float) * components);\n            value_changed = accepted_drag_drop = true;\n        }\n\n        // Drag-drop payloads are always RGB\n        if (accepted_drag_drop && (flags & ImGuiColorEditFlags_InputHSV))\n            ColorConvertRGBtoHSV(col[0], col[1], col[2], col[0], col[1], col[2]);\n        EndDragDropTarget();\n    }\n\n    // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4().\n    if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window)\n        g.LastItemData.ID = g.ActiveId;\n\n    if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId\n        MarkItemEdited(g.LastItemData.ID);\n\n    return value_changed;\n}\n\nbool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags)\n{\n    float col4[4] = { col[0], col[1], col[2], 1.0f };\n    if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha))\n        return false;\n    col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2];\n    return true;\n}\n\n// Helper for ColorPicker4()\nstatic void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w, float alpha)\n{\n    ImU32 alpha8 = IM_F32_TO_INT8_SAT(alpha);\n    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x + 1,         pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32(0,0,0,alpha8));\n    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + half_sz.x,             pos.y), half_sz,                              ImGuiDir_Right, IM_COL32(255,255,255,alpha8));\n    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left,  IM_COL32(0,0,0,alpha8));\n    ImGui::RenderArrowPointingAt(draw_list, ImVec2(pos.x + bar_w - half_sz.x,     pos.y), half_sz,                              ImGuiDir_Left,  IM_COL32(255,255,255,alpha8));\n}\n\n// Note: ColorPicker4() only accesses 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.\n// (In C++ the 'float col[4]' notation for a function argument is equivalent to 'float* col', we only specify a size to facilitate understanding of the code.)\n// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..)\n// FIXME: this is trying to be aware of style.Alpha but not fully correct. Also, the color wheel will have overlapping glitches with (style.Alpha < 1.0)\nbool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImDrawList* draw_list = window->DrawList;\n    ImGuiStyle& style = g.Style;\n    ImGuiIO& io = g.IO;\n\n    const float width = CalcItemWidth();\n    g.NextItemData.ClearFlags();\n\n    PushID(label);\n    const bool set_current_color_edit_id = (g.ColorEditCurrentID == 0);\n    if (set_current_color_edit_id)\n        g.ColorEditCurrentID = window->IDStack.back();\n    BeginGroup();\n\n    if (!(flags & ImGuiColorEditFlags_NoSidePreview))\n        flags |= ImGuiColorEditFlags_NoSmallPreview;\n\n    // Context menu: display and store options.\n    if (!(flags & ImGuiColorEditFlags_NoOptions))\n        ColorPickerOptionsPopup(col, flags);\n\n    // Read stored options\n    if (!(flags & ImGuiColorEditFlags_PickerMask_))\n        flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_PickerMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_PickerMask_;\n    if (!(flags & ImGuiColorEditFlags_InputMask_))\n        flags |= ((g.ColorEditOptions & ImGuiColorEditFlags_InputMask_) ? g.ColorEditOptions : ImGuiColorEditFlags_DefaultOptions_) & ImGuiColorEditFlags_InputMask_;\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_)); // Check that only 1 is selected\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));  // Check that only 1 is selected\n    if (!(flags & ImGuiColorEditFlags_NoOptions))\n        flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar);\n\n    // Setup\n    int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4;\n    bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha);\n    ImVec2 picker_pos = window->DC.CursorPos;\n    float square_sz = GetFrameHeight();\n    float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars\n    float sv_picker_size = ImMax(bars_width * 1, width - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box\n    float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x;\n    float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x;\n    float bars_triangles_half_sz = IM_TRUNC(bars_width * 0.20f);\n\n    float backup_initial_col[4];\n    memcpy(backup_initial_col, col, components * sizeof(float));\n\n    float wheel_thickness = sv_picker_size * 0.08f;\n    float wheel_r_outer = sv_picker_size * 0.50f;\n    float wheel_r_inner = wheel_r_outer - wheel_thickness;\n    ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size * 0.5f);\n\n    // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic.\n    float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f);\n    ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point.\n    ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point.\n    ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point.\n\n    float H = col[0], S = col[1], V = col[2];\n    float R = col[0], G = col[1], B = col[2];\n    if (flags & ImGuiColorEditFlags_InputRGB)\n    {\n        // Hue is lost when converting from grayscale rgb (saturation=0). Restore it.\n        ColorConvertRGBtoHSV(R, G, B, H, S, V);\n        ColorEditRestoreHS(col, &H, &S, &V);\n    }\n    else if (flags & ImGuiColorEditFlags_InputHSV)\n    {\n        ColorConvertHSVtoRGB(H, S, V, R, G, B);\n    }\n\n    bool value_changed = false, value_changed_h = false, value_changed_sv = false;\n\n    PushItemFlag(ImGuiItemFlags_NoNav, true);\n    if (flags & ImGuiColorEditFlags_PickerHueWheel)\n    {\n        // Hue wheel + SV triangle logic\n        InvisibleButton(\"hsv\", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size));\n        if (IsItemActive())\n        {\n            ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center;\n            ImVec2 current_off = g.IO.MousePos - wheel_center;\n            float initial_dist2 = ImLengthSqr(initial_off);\n            if (initial_dist2 >= (wheel_r_inner - 1) * (wheel_r_inner - 1) && initial_dist2 <= (wheel_r_outer + 1) * (wheel_r_outer + 1))\n            {\n                // Interactive with Hue wheel\n                H = ImAtan2(current_off.y, current_off.x) / IM_PI * 0.5f;\n                if (H < 0.0f)\n                    H += 1.0f;\n                value_changed = value_changed_h = true;\n            }\n            float cos_hue_angle = ImCos(-H * 2.0f * IM_PI);\n            float sin_hue_angle = ImSin(-H * 2.0f * IM_PI);\n            if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle)))\n            {\n                // Interacting with SV triangle\n                ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle);\n                if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated))\n                    current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated);\n                float uu, vv, ww;\n                ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww);\n                V = ImClamp(1.0f - vv, 0.0001f, 1.0f);\n                S = ImClamp(uu / V, 0.0001f, 1.0f);\n                value_changed = value_changed_sv = true;\n            }\n        }\n        if (!(flags & ImGuiColorEditFlags_NoOptions))\n            OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n    }\n    else if (flags & ImGuiColorEditFlags_PickerHueBar)\n    {\n        // SV rectangle logic\n        InvisibleButton(\"sv\", ImVec2(sv_picker_size, sv_picker_size));\n        if (IsItemActive())\n        {\n            S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size - 1));\n            V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));\n            ColorEditRestoreH(col, &H); // Greatly reduces hue jitter and reset to 0 when hue == 255 and color is rapidly modified using SV square.\n            value_changed = value_changed_sv = true;\n        }\n        if (!(flags & ImGuiColorEditFlags_NoOptions))\n            OpenPopupOnItemClick(\"context\", ImGuiPopupFlags_MouseButtonRight);\n\n        // Hue bar logic\n        SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y));\n        InvisibleButton(\"hue\", ImVec2(bars_width, sv_picker_size));\n        if (IsItemActive())\n        {\n            H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));\n            value_changed = value_changed_h = true;\n        }\n    }\n\n    // Alpha bar logic\n    if (alpha_bar)\n    {\n        SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y));\n        InvisibleButton(\"alpha\", ImVec2(bars_width, sv_picker_size));\n        if (IsItemActive())\n        {\n            col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size - 1));\n            value_changed = true;\n        }\n    }\n    PopItemFlag(); // ImGuiItemFlags_NoNav\n\n    if (!(flags & ImGuiColorEditFlags_NoSidePreview))\n    {\n        SameLine(0, style.ItemInnerSpacing.x);\n        BeginGroup();\n    }\n\n    if (!(flags & ImGuiColorEditFlags_NoLabel))\n    {\n        const char* label_display_end = FindRenderedTextEnd(label);\n        if (label != label_display_end)\n        {\n            if ((flags & ImGuiColorEditFlags_NoSidePreview))\n                SameLine(0, style.ItemInnerSpacing.x);\n            TextEx(label, label_display_end);\n        }\n    }\n\n    if (!(flags & ImGuiColorEditFlags_NoSidePreview))\n    {\n        PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true);\n        ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);\n        if ((flags & ImGuiColorEditFlags_NoLabel))\n            Text(\"Current\");\n\n        ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip;\n        ColorButton(\"##current\", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2));\n        if (ref_col != NULL)\n        {\n            Text(\"Original\");\n            ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]);\n            if (ColorButton(\"##original\", ref_col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)))\n            {\n                memcpy(col, ref_col, components * sizeof(float));\n                value_changed = true;\n            }\n        }\n        PopItemFlag();\n        EndGroup();\n    }\n\n    // Convert back color to RGB\n    if (value_changed_h || value_changed_sv)\n    {\n        if (flags & ImGuiColorEditFlags_InputRGB)\n        {\n            ColorConvertHSVtoRGB(H, S, V, col[0], col[1], col[2]);\n            g.ColorEditSavedHue = H;\n            g.ColorEditSavedSat = S;\n            g.ColorEditSavedID = g.ColorEditCurrentID;\n            g.ColorEditSavedColor = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 0));\n        }\n        else if (flags & ImGuiColorEditFlags_InputHSV)\n        {\n            col[0] = H;\n            col[1] = S;\n            col[2] = V;\n        }\n    }\n\n    // R,G,B and H,S,V slider color editor\n    bool value_changed_fix_hue_wrap = false;\n    if ((flags & ImGuiColorEditFlags_NoInputs) == 0)\n    {\n        PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x);\n        ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf;\n        ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker;\n        if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)\n            if (ColorEdit4(\"##rgb\", col, sub_flags | ImGuiColorEditFlags_DisplayRGB))\n            {\n                // FIXME: Hackily differentiating using the DragInt (ActiveId != 0 && !ActiveIdAllowOverlap) vs. using the InputText or DropTarget.\n                // For the later we don't want to run the hue-wrap canceling code. If you are well versed in HSV picker please provide your input! (See #2050)\n                value_changed_fix_hue_wrap = (g.ActiveId != 0 && !g.ActiveIdAllowOverlap);\n                value_changed = true;\n            }\n        if (flags & ImGuiColorEditFlags_DisplayHSV || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)\n            value_changed |= ColorEdit4(\"##hsv\", col, sub_flags | ImGuiColorEditFlags_DisplayHSV);\n        if (flags & ImGuiColorEditFlags_DisplayHex || (flags & ImGuiColorEditFlags_DisplayMask_) == 0)\n            value_changed |= ColorEdit4(\"##hex\", col, sub_flags | ImGuiColorEditFlags_DisplayHex);\n        PopItemWidth();\n    }\n\n    // Try to cancel hue wrap (after ColorEdit4 call), if any\n    if (value_changed_fix_hue_wrap && (flags & ImGuiColorEditFlags_InputRGB))\n    {\n        float new_H, new_S, new_V;\n        ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V);\n        if (new_H <= 0 && H > 0)\n        {\n            if (new_V <= 0 && V != new_V)\n                ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]);\n            else if (new_S <= 0)\n                ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]);\n        }\n    }\n\n    if (value_changed)\n    {\n        if (flags & ImGuiColorEditFlags_InputRGB)\n        {\n            R = col[0];\n            G = col[1];\n            B = col[2];\n            ColorConvertRGBtoHSV(R, G, B, H, S, V);\n            ColorEditRestoreHS(col, &H, &S, &V);   // Fix local Hue as display below will use it immediately.\n        }\n        else if (flags & ImGuiColorEditFlags_InputHSV)\n        {\n            H = col[0];\n            S = col[1];\n            V = col[2];\n            ColorConvertHSVtoRGB(H, S, V, R, G, B);\n        }\n    }\n\n    const int style_alpha8 = IM_F32_TO_INT8_SAT(style.Alpha);\n    const ImU32 col_black = IM_COL32(0,0,0,style_alpha8);\n    const ImU32 col_white = IM_COL32(255,255,255,style_alpha8);\n    const ImU32 col_midgrey = IM_COL32(128,128,128,style_alpha8);\n    const ImU32 col_hues[6 + 1] = { IM_COL32(255,0,0,style_alpha8), IM_COL32(255,255,0,style_alpha8), IM_COL32(0,255,0,style_alpha8), IM_COL32(0,255,255,style_alpha8), IM_COL32(0,0,255,style_alpha8), IM_COL32(255,0,255,style_alpha8), IM_COL32(255,0,0,style_alpha8) };\n\n    ImVec4 hue_color_f(1, 1, 1, style.Alpha); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z);\n    ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f);\n    ImU32 user_col32_striped_of_alpha = ColorConvertFloat4ToU32(ImVec4(R, G, B, style.Alpha)); // Important: this is still including the main rendering/style alpha!!\n\n    ImVec2 sv_cursor_pos;\n\n    if (flags & ImGuiColorEditFlags_PickerHueWheel)\n    {\n        // Render Hue Wheel\n        const float aeps = 0.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out).\n        const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12);\n        for (int n = 0; n < 6; n++)\n        {\n            const float a0 = (n)     /6.0f * 2.0f * IM_PI - aeps;\n            const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps;\n            const int vert_start_idx = draw_list->VtxBuffer.Size;\n            draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc);\n            draw_list->PathStroke(col_white, 0, wheel_thickness);\n            const int vert_end_idx = draw_list->VtxBuffer.Size;\n\n            // Paint colors over existing vertices\n            ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner);\n            ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner);\n            ShadeVertsLinearColorGradientKeepAlpha(draw_list, vert_start_idx, vert_end_idx, gradient_p0, gradient_p1, col_hues[n], col_hues[n + 1]);\n        }\n\n        // Render Cursor + preview on Hue Wheel\n        float cos_hue_angle = ImCos(H * 2.0f * IM_PI);\n        float sin_hue_angle = ImSin(H * 2.0f * IM_PI);\n        ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner + wheel_r_outer) * 0.5f);\n        float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f;\n        int hue_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(hue_cursor_rad); // Lock segment count so the +1 one matches others.\n        draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments);\n        draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad + 1, col_midgrey, hue_cursor_segments);\n        draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, col_white, hue_cursor_segments);\n\n        // Render SV triangle (rotated according to hue)\n        ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle);\n        ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle);\n        ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle);\n        ImVec2 uv_white = GetFontTexUvWhitePixel();\n        draw_list->PrimReserve(3, 3);\n        draw_list->PrimVtx(tra, uv_white, hue_color32);\n        draw_list->PrimVtx(trb, uv_white, col_black);\n        draw_list->PrimVtx(trc, uv_white, col_white);\n        draw_list->AddTriangle(tra, trb, trc, col_midgrey, 1.5f);\n        sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V));\n    }\n    else if (flags & ImGuiColorEditFlags_PickerHueBar)\n    {\n        // Render SV Square\n        draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), col_white, hue_color32, hue_color32, col_white);\n        draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0, 0, col_black, col_black);\n        RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size, sv_picker_size), 0.0f);\n        sv_cursor_pos.x = ImClamp(IM_ROUND(picker_pos.x + ImSaturate(S)     * sv_picker_size), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much\n        sv_cursor_pos.y = ImClamp(IM_ROUND(picker_pos.y + ImSaturate(1 - V) * sv_picker_size), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2);\n\n        // Render Hue Bar\n        for (int i = 0; i < 6; ++i)\n            draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), col_hues[i], col_hues[i], col_hues[i + 1], col_hues[i + 1]);\n        float bar0_line_y = IM_ROUND(picker_pos.y + H * sv_picker_size);\n        RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f);\n        RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha);\n    }\n\n    // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range)\n    float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f;\n    int sv_cursor_segments = draw_list->_CalcCircleAutoSegmentCount(sv_cursor_rad); // Lock segment count so the +1 one matches others.\n    draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, user_col32_striped_of_alpha, sv_cursor_segments);\n    draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad + 1, col_midgrey, sv_cursor_segments);\n    draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, col_white, sv_cursor_segments);\n\n    // Render alpha bar\n    if (alpha_bar)\n    {\n        float alpha = ImSaturate(col[3]);\n        ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size);\n        RenderColorRectWithAlphaCheckerboard(draw_list, bar1_bb.Min, bar1_bb.Max, 0, bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f));\n        draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, user_col32_striped_of_alpha, user_col32_striped_of_alpha, user_col32_striped_of_alpha & ~IM_COL32_A_MASK, user_col32_striped_of_alpha & ~IM_COL32_A_MASK);\n        float bar1_line_y = IM_ROUND(picker_pos.y + (1.0f - alpha) * sv_picker_size);\n        RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f);\n        RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f, style.Alpha);\n    }\n\n    EndGroup();\n\n    if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0)\n        value_changed = false;\n    if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId\n        MarkItemEdited(g.LastItemData.ID);\n\n    if (set_current_color_edit_id)\n        g.ColorEditCurrentID = 0;\n    PopID();\n\n    return value_changed;\n}\n\n// A little color square. Return true when clicked.\n// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip.\n// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip.\n// Note that 'col' may be encoded in HSV if ImGuiColorEditFlags_InputHSV is set.\nbool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, const ImVec2& size_arg)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiID id = window->GetID(desc_id);\n    const float default_size = GetFrameHeight();\n    const ImVec2 size(size_arg.x == 0.0f ? default_size : size_arg.x, size_arg.y == 0.0f ? default_size : size_arg.y);\n    const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size);\n    ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f);\n    if (!ItemAdd(bb, id))\n        return false;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held);\n\n    if (flags & ImGuiColorEditFlags_NoAlpha)\n        flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf);\n\n    ImVec4 col_rgb = col;\n    if (flags & ImGuiColorEditFlags_InputHSV)\n        ColorConvertHSVtoRGB(col_rgb.x, col_rgb.y, col_rgb.z, col_rgb.x, col_rgb.y, col_rgb.z);\n\n    ImVec4 col_rgb_without_alpha(col_rgb.x, col_rgb.y, col_rgb.z, 1.0f);\n    float grid_step = ImMin(size.x, size.y) / 2.99f;\n    float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f);\n    ImRect bb_inner = bb;\n    float off = 0.0f;\n    if ((flags & ImGuiColorEditFlags_NoBorder) == 0)\n    {\n        off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts.\n        bb_inner.Expand(off);\n    }\n    if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f)\n    {\n        float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f);\n        RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight);\n        window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft);\n    }\n    else\n    {\n        // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha\n        ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col_rgb : col_rgb_without_alpha;\n        if (col_source.w < 1.0f)\n            RenderColorRectWithAlphaCheckerboard(window->DrawList, bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding);\n        else\n            window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding);\n    }\n    RenderNavHighlight(bb, id);\n    if ((flags & ImGuiColorEditFlags_NoBorder) == 0)\n    {\n        if (g.Style.FrameBorderSize > 0.0f)\n            RenderFrameBorder(bb.Min, bb.Max, rounding);\n        else\n            window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border\n    }\n\n    // Drag and Drop Source\n    // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test.\n    if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource())\n    {\n        if (flags & ImGuiColorEditFlags_NoAlpha)\n            SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col_rgb, sizeof(float) * 3, ImGuiCond_Once);\n        else\n            SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col_rgb, sizeof(float) * 4, ImGuiCond_Once);\n        ColorButton(desc_id, col, flags);\n        SameLine();\n        TextEx(\"Color\");\n        EndDragDropSource();\n    }\n\n    // Tooltip\n    if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip))\n        ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf));\n\n    return pressed;\n}\n\n// Initialize/override default color options\nvoid ImGui::SetColorEditOptions(ImGuiColorEditFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    if ((flags & ImGuiColorEditFlags_DisplayMask_) == 0)\n        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DisplayMask_;\n    if ((flags & ImGuiColorEditFlags_DataTypeMask_) == 0)\n        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_DataTypeMask_;\n    if ((flags & ImGuiColorEditFlags_PickerMask_) == 0)\n        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_PickerMask_;\n    if ((flags & ImGuiColorEditFlags_InputMask_) == 0)\n        flags |= ImGuiColorEditFlags_DefaultOptions_ & ImGuiColorEditFlags_InputMask_;\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DisplayMask_));    // Check only 1 option is selected\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_DataTypeMask_));   // Check only 1 option is selected\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_PickerMask_));     // Check only 1 option is selected\n    IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiColorEditFlags_InputMask_));      // Check only 1 option is selected\n    g.ColorEditOptions = flags;\n}\n\n// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.\nvoid ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n\n    if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))\n        return;\n    const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text;\n    if (text_end > text)\n    {\n        TextEx(text, text_end);\n        Separator();\n    }\n\n    ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2);\n    ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);\n    int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);\n    ColorButton(\"##preview\", cf, (flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz);\n    SameLine();\n    if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_))\n    {\n        if (flags & ImGuiColorEditFlags_NoAlpha)\n            Text(\"#%02X%02X%02X\\nR: %d, G: %d, B: %d\\n(%.3f, %.3f, %.3f)\", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]);\n        else\n            Text(\"#%02X%02X%02X%02X\\nR:%d, G:%d, B:%d, A:%d\\n(%.3f, %.3f, %.3f, %.3f)\", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]);\n    }\n    else if (flags & ImGuiColorEditFlags_InputHSV)\n    {\n        if (flags & ImGuiColorEditFlags_NoAlpha)\n            Text(\"H: %.3f, S: %.3f, V: %.3f\", col[0], col[1], col[2]);\n        else\n            Text(\"H: %.3f, S: %.3f, V: %.3f, A: %.3f\", col[0], col[1], col[2], col[3]);\n    }\n    EndTooltip();\n}\n\nvoid ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags)\n{\n    bool allow_opt_inputs = !(flags & ImGuiColorEditFlags_DisplayMask_);\n    bool allow_opt_datatype = !(flags & ImGuiColorEditFlags_DataTypeMask_);\n    if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup(\"context\"))\n        return;\n    ImGuiContext& g = *GImGui;\n    g.LockMarkEdited++;\n    ImGuiColorEditFlags opts = g.ColorEditOptions;\n    if (allow_opt_inputs)\n    {\n        if (RadioButton(\"RGB\", (opts & ImGuiColorEditFlags_DisplayRGB) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayRGB;\n        if (RadioButton(\"HSV\", (opts & ImGuiColorEditFlags_DisplayHSV) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHSV;\n        if (RadioButton(\"Hex\", (opts & ImGuiColorEditFlags_DisplayHex) != 0)) opts = (opts & ~ImGuiColorEditFlags_DisplayMask_) | ImGuiColorEditFlags_DisplayHex;\n    }\n    if (allow_opt_datatype)\n    {\n        if (allow_opt_inputs) Separator();\n        if (RadioButton(\"0..255\",     (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Uint8;\n        if (RadioButton(\"0.00..1.00\", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags_DataTypeMask_) | ImGuiColorEditFlags_Float;\n    }\n\n    if (allow_opt_inputs || allow_opt_datatype)\n        Separator();\n    if (Button(\"Copy as..\", ImVec2(-1, 0)))\n        OpenPopup(\"Copy\");\n    if (BeginPopup(\"Copy\"))\n    {\n        int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]);\n        char buf[64];\n        ImFormatString(buf, IM_ARRAYSIZE(buf), \"(%.3ff, %.3ff, %.3ff, %.3ff)\", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]);\n        if (Selectable(buf))\n            SetClipboardText(buf);\n        ImFormatString(buf, IM_ARRAYSIZE(buf), \"(%d,%d,%d,%d)\", cr, cg, cb, ca);\n        if (Selectable(buf))\n            SetClipboardText(buf);\n        ImFormatString(buf, IM_ARRAYSIZE(buf), \"#%02X%02X%02X\", cr, cg, cb);\n        if (Selectable(buf))\n            SetClipboardText(buf);\n        if (!(flags & ImGuiColorEditFlags_NoAlpha))\n        {\n            ImFormatString(buf, IM_ARRAYSIZE(buf), \"#%02X%02X%02X%02X\", cr, cg, cb, ca);\n            if (Selectable(buf))\n                SetClipboardText(buf);\n        }\n        EndPopup();\n    }\n\n    g.ColorEditOptions = opts;\n    EndPopup();\n    g.LockMarkEdited--;\n}\n\nvoid ImGui::ColorPickerOptionsPopup(const float* ref_col, ImGuiColorEditFlags flags)\n{\n    bool allow_opt_picker = !(flags & ImGuiColorEditFlags_PickerMask_);\n    bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar);\n    if ((!allow_opt_picker && !allow_opt_alpha_bar) || !BeginPopup(\"context\"))\n        return;\n    ImGuiContext& g = *GImGui;\n    g.LockMarkEdited++;\n    if (allow_opt_picker)\n    {\n        ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function\n        PushItemWidth(picker_size.x);\n        for (int picker_type = 0; picker_type < 2; picker_type++)\n        {\n            // Draw small/thumbnail version of each picker type (over an invisible button for selection)\n            if (picker_type > 0) Separator();\n            PushID(picker_type);\n            ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoSidePreview | (flags & ImGuiColorEditFlags_NoAlpha);\n            if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar;\n            if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel;\n            ImVec2 backup_pos = GetCursorScreenPos();\n            if (Selectable(\"##selectable\", false, 0, picker_size)) // By default, Selectable() is closing popup\n                g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags_PickerMask_) | (picker_flags & ImGuiColorEditFlags_PickerMask_);\n            SetCursorScreenPos(backup_pos);\n            ImVec4 previewing_ref_col;\n            memcpy(&previewing_ref_col, ref_col, sizeof(float) * ((picker_flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4));\n            ColorPicker4(\"##previewing_picker\", &previewing_ref_col.x, picker_flags);\n            PopID();\n        }\n        PopItemWidth();\n    }\n    if (allow_opt_alpha_bar)\n    {\n        if (allow_opt_picker) Separator();\n        CheckboxFlags(\"Alpha Bar\", &g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar);\n    }\n    EndPopup();\n    g.LockMarkEdited--;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: TreeNode, CollapsingHeader, etc.\n//-------------------------------------------------------------------------\n// - TreeNode()\n// - TreeNodeV()\n// - TreeNodeEx()\n// - TreeNodeExV()\n// - TreeNodeBehavior() [Internal]\n// - TreePush()\n// - TreePop()\n// - GetTreeNodeToLabelSpacing()\n// - SetNextItemOpen()\n// - CollapsingHeader()\n//-------------------------------------------------------------------------\n\nbool ImGui::TreeNode(const char* str_id, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool is_open = TreeNodeExV(str_id, 0, fmt, args);\n    va_end(args);\n    return is_open;\n}\n\nbool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);\n    va_end(args);\n    return is_open;\n}\n\nbool ImGui::TreeNode(const char* label)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n    return TreeNodeBehavior(window->GetID(label), 0, label, NULL);\n}\n\nbool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)\n{\n    return TreeNodeExV(str_id, 0, fmt, args);\n}\n\nbool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)\n{\n    return TreeNodeExV(ptr_id, 0, fmt, args);\n}\n\nbool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    return TreeNodeBehavior(window->GetID(label), flags, label, NULL);\n}\n\nbool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool is_open = TreeNodeExV(str_id, flags, fmt, args);\n    va_end(args);\n    return is_open;\n}\n\nbool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);\n    va_end(args);\n    return is_open;\n}\n\nbool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    const char* label, *label_end;\n    ImFormatStringToTempBufferV(&label, &label_end, fmt, args);\n    return TreeNodeBehavior(window->GetID(str_id), flags, label, label_end);\n}\n\nbool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    const char* label, *label_end;\n    ImFormatStringToTempBufferV(&label, &label_end, fmt, args);\n    return TreeNodeBehavior(window->GetID(ptr_id), flags, label, label_end);\n}\n\nvoid ImGui::TreeNodeSetOpen(ImGuiID id, bool open)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage;\n    storage->SetInt(id, open ? 1 : 0);\n}\n\nbool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags)\n{\n    if (flags & ImGuiTreeNodeFlags_Leaf)\n        return true;\n\n    // We only write to the tree storage if the user clicks (or explicitly use the SetNextItemOpen function)\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    ImGuiStorage* storage = window->DC.StateStorage;\n\n    bool is_open;\n    if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasOpen)\n    {\n        if (g.NextItemData.OpenCond & ImGuiCond_Always)\n        {\n            is_open = g.NextItemData.OpenVal;\n            TreeNodeSetOpen(id, is_open);\n        }\n        else\n        {\n            // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.\n            const int stored_value = storage->GetInt(id, -1);\n            if (stored_value == -1)\n            {\n                is_open = g.NextItemData.OpenVal;\n                TreeNodeSetOpen(id, is_open);\n            }\n            else\n            {\n                is_open = stored_value != 0;\n            }\n        }\n    }\n    else\n    {\n        is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;\n    }\n\n    // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior).\n    // NB- If we are above max depth we still allow manually opened nodes to be logged.\n    if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && (window->DC.TreeDepth - g.LogDepthRef) < g.LogDepthToExpand)\n        is_open = true;\n\n    return is_open;\n}\n\nbool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;\n    const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y));\n\n    if (!label_end)\n        label_end = FindRenderedTextEnd(label);\n    const ImVec2 label_size = CalcTextSize(label, label_end, false);\n\n    // We vertically grow up to current line height up the typical widget height.\n    const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2);\n    const bool span_all_columns = (flags & ImGuiTreeNodeFlags_SpanAllColumns) != 0 && (g.CurrentTable != NULL);\n    ImRect frame_bb;\n    frame_bb.Min.x = span_all_columns ? window->ParentWorkRect.Min.x : (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x;\n    frame_bb.Min.y = window->DC.CursorPos.y;\n    frame_bb.Max.x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;\n    frame_bb.Max.y = window->DC.CursorPos.y + frame_height;\n    if (display_frame)\n    {\n        // Framed header expand a little outside the default padding, to the edge of InnerClipRect\n        // (FIXME: May remove this at some point and make InnerClipRect align with WindowPadding.x instead of WindowPadding.x*0.5f)\n        frame_bb.Min.x -= IM_TRUNC(window->WindowPadding.x * 0.5f - 1.0f);\n        frame_bb.Max.x += IM_TRUNC(window->WindowPadding.x * 0.5f);\n    }\n\n    const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2);           // Collapsing arrow width + Spacing\n    const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset);                    // Latch before ItemSize changes it\n    const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f);  // Include collapsing\n    ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y);\n    ItemSize(ImVec2(text_width, frame_height), padding.y);\n\n    // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing\n    ImRect interact_bb = frame_bb;\n    if (!display_frame && (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0)\n        interact_bb.Max.x = frame_bb.Min.x + text_width + style.ItemSpacing.x * 2.0f;\n\n    // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..\n    const float backup_clip_rect_min_x = window->ClipRect.Min.x;\n    const float backup_clip_rect_max_x = window->ClipRect.Max.x;\n    if (span_all_columns)\n    {\n        window->ClipRect.Min.x = window->ParentWorkRect.Min.x;\n        window->ClipRect.Max.x = window->ParentWorkRect.Max.x;\n    }\n\n    // Compute open and multi-select states before ItemAdd() as it clear NextItem data.\n    bool is_open = TreeNodeUpdateNextOpen(id, flags);\n    bool item_add = ItemAdd(interact_bb, id);\n    g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;\n    g.LastItemData.DisplayRect = frame_bb;\n\n    if (span_all_columns)\n    {\n        window->ClipRect.Min.x = backup_clip_rect_min_x;\n        window->ClipRect.Max.x = backup_clip_rect_max_x;\n    }\n\n    // If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled:\n    // Store data for the current depth to allow returning to this node from any child item.\n    // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop().\n    // It will become tempting to enable ImGuiTreeNodeFlags_NavLeftJumpsBackHere by default or move it to ImGuiStyle.\n    // Currently only supports 32 level deep and we are fine with (1 << Depth) overflowing into a zero, easy to increase.\n    if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))\n        if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())\n        {\n            g.NavTreeNodeStack.resize(g.NavTreeNodeStack.Size + 1);\n            ImGuiNavTreeNodeData* nav_tree_node_data = &g.NavTreeNodeStack.back();\n            nav_tree_node_data->ID = id;\n            nav_tree_node_data->InFlags = g.LastItemData.InFlags;\n            nav_tree_node_data->NavRect = g.LastItemData.NavRect;\n            window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth);\n        }\n\n    const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;\n    if (!item_add)\n    {\n        if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))\n            TreePushOverrideID(id);\n        IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));\n        return is_open;\n    }\n\n    if (span_all_columns)\n        TablePushBackgroundChannel();\n\n    ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None;\n    if ((flags & ImGuiTreeNodeFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap))\n        button_flags |= ImGuiButtonFlags_AllowOverlap;\n    if (!is_leaf)\n        button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;\n\n    // We allow clicking on the arrow section with keyboard modifiers held, in order to easily\n    // allow browsing a tree while preserving selection with code implementing multi-selection patterns.\n    // When clicking on the rest of the tree node we always disallow keyboard modifiers.\n    const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x;\n    const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x;\n    const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2);\n    if (window != g.HoveredWindow || !is_mouse_x_over_arrow)\n        button_flags |= ImGuiButtonFlags_NoKeyModifiers;\n\n    // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags.\n    // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support.\n    // - Single-click on label = Toggle on MouseUp (default, when _OpenOnArrow=0)\n    // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=0)\n    // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=1)\n    // - Double-click on label = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1)\n    // - Double-click on arrow = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1 and _OpenOnArrow=0)\n    // It is rather standard that arrow click react on Down rather than Up.\n    // We set ImGuiButtonFlags_PressedOnClickRelease on OpenOnDoubleClick because we want the item to be active on the initial MouseDown in order for drag and drop to work.\n    if (is_mouse_x_over_arrow)\n        button_flags |= ImGuiButtonFlags_PressedOnClick;\n    else if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)\n        button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;\n    else\n        button_flags |= ImGuiButtonFlags_PressedOnClickRelease;\n\n    bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0;\n    const bool was_selected = selected;\n\n    bool hovered, held;\n    bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);\n    bool toggled = false;\n    if (!is_leaf)\n    {\n        if (pressed && g.DragDropHoldJustPressedId != id)\n        {\n            if ((flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) == 0 || (g.NavActivateId == id))\n                toggled = true;\n            if (flags & ImGuiTreeNodeFlags_OpenOnArrow)\n                toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job\n            if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseClickedCount[0] == 2)\n                toggled = true;\n        }\n        else if (pressed && g.DragDropHoldJustPressedId == id)\n        {\n            IM_ASSERT(button_flags & ImGuiButtonFlags_PressedOnDragDropHold);\n            if (!is_open) // When using Drag and Drop \"hold to open\" we keep the node highlighted after opening, but never close it again.\n                toggled = true;\n        }\n\n        if (g.NavId == id && g.NavMoveDir == ImGuiDir_Left && is_open)\n        {\n            toggled = true;\n            NavClearPreferredPosForAxis(ImGuiAxis_X);\n            NavMoveRequestCancel();\n        }\n        if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority?\n        {\n            toggled = true;\n            NavClearPreferredPosForAxis(ImGuiAxis_X);\n            NavMoveRequestCancel();\n        }\n\n        if (toggled)\n        {\n            is_open = !is_open;\n            window->DC.StateStorage->SetInt(id, is_open);\n            g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledOpen;\n        }\n    }\n\n    // In this branch, TreeNodeBehavior() cannot toggle the selection so this will never trigger.\n    if (selected != was_selected) //-V547\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;\n\n    // Render\n    const ImU32 text_col = GetColorU32(ImGuiCol_Text);\n    ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_TypeThin;\n    if (display_frame)\n    {\n        // Framed type\n        const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);\n        RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding);\n        RenderNavHighlight(frame_bb, id, nav_highlight_flags);\n        if (flags & ImGuiTreeNodeFlags_Bullet)\n            RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col);\n        else if (!is_leaf)\n            RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f);\n        else // Leaf without bullet, left-adjusted text\n            text_pos.x -= text_offset_x -padding.x;\n        if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton)\n            frame_bb.Max.x -= g.FontSize + style.FramePadding.x;\n\n        if (g.LogEnabled)\n            LogSetNextTextDecoration(\"###\", \"###\");\n    }\n    else\n    {\n        // Unframed typed for tree nodes\n        if (hovered || selected)\n        {\n            const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);\n            RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false);\n        }\n        RenderNavHighlight(frame_bb, id, nav_highlight_flags);\n        if (flags & ImGuiTreeNodeFlags_Bullet)\n            RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col);\n        else if (!is_leaf)\n            RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f);\n        if (g.LogEnabled)\n            LogSetNextTextDecoration(\">\", NULL);\n    }\n\n    if (span_all_columns)\n        TablePopBackgroundChannel();\n\n    // Label\n    if (display_frame)\n        RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);\n    else\n        RenderText(text_pos, label, label_end, false);\n\n    if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))\n        TreePushOverrideID(id);\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0));\n    return is_open;\n}\n\nvoid ImGui::TreePush(const char* str_id)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    Indent();\n    window->DC.TreeDepth++;\n    PushID(str_id);\n}\n\nvoid ImGui::TreePush(const void* ptr_id)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    Indent();\n    window->DC.TreeDepth++;\n    PushID(ptr_id);\n}\n\nvoid ImGui::TreePushOverrideID(ImGuiID id)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    Indent();\n    window->DC.TreeDepth++;\n    PushOverrideID(id);\n}\n\nvoid ImGui::TreePop()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    Unindent();\n\n    window->DC.TreeDepth--;\n    ImU32 tree_depth_mask = (1 << window->DC.TreeDepth);\n\n    // Handle Left arrow to move to parent tree node (when ImGuiTreeNodeFlags_NavLeftJumpsBackHere is enabled)\n    if (window->DC.TreeJumpToParentOnPopMask & tree_depth_mask) // Only set during request\n    {\n        ImGuiNavTreeNodeData* nav_tree_node_data = &g.NavTreeNodeStack.back();\n        IM_ASSERT(nav_tree_node_data->ID == window->IDStack.back());\n        if (g.NavIdIsAlive && g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet())\n            NavMoveRequestResolveWithPastTreeNode(&g.NavMoveResultLocal, nav_tree_node_data);\n        g.NavTreeNodeStack.pop_back();\n    }\n    window->DC.TreeJumpToParentOnPopMask &= tree_depth_mask - 1;\n\n    IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.\n    PopID();\n}\n\n// Horizontal distance preceding label when using TreeNode() or Bullet()\nfloat ImGui::GetTreeNodeToLabelSpacing()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize + (g.Style.FramePadding.x * 2.0f);\n}\n\n// Set next TreeNode/CollapsingHeader open state.\nvoid ImGui::SetNextItemOpen(bool is_open, ImGuiCond cond)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.CurrentWindow->SkipItems)\n        return;\n    g.NextItemData.Flags |= ImGuiNextItemDataFlags_HasOpen;\n    g.NextItemData.OpenVal = is_open;\n    g.NextItemData.OpenCond = cond ? cond : ImGuiCond_Always;\n}\n\n// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).\n// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().\nbool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label);\n}\n\n// p_visible == NULL                        : regular collapsing header\n// p_visible != NULL && *p_visible == true  : show a small close button on the corner of the header, clicking the button will set *p_visible = false\n// p_visible != NULL && *p_visible == false : do not show the header at all\n// Do not mistake this with the Open state of the header itself, which you can adjust with SetNextItemOpen() or ImGuiTreeNodeFlags_DefaultOpen.\nbool ImGui::CollapsingHeader(const char* label, bool* p_visible, ImGuiTreeNodeFlags flags)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    if (p_visible && !*p_visible)\n        return false;\n\n    ImGuiID id = window->GetID(label);\n    flags |= ImGuiTreeNodeFlags_CollapsingHeader;\n    if (p_visible)\n        flags |= ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_ClipLabelForTrailingButton;\n    bool is_open = TreeNodeBehavior(id, flags, label);\n    if (p_visible != NULL)\n    {\n        // Create a small overlapping close button\n        // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc.\n        // FIXME: CloseButton can overlap into text, need find a way to clip the text somehow.\n        ImGuiContext& g = *GImGui;\n        ImGuiLastItemData last_item_backup = g.LastItemData;\n        float button_size = g.FontSize;\n        float button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x - button_size);\n        float button_y = g.LastItemData.Rect.Min.y + g.Style.FramePadding.y;\n        ImGuiID close_button_id = GetIDWithSeed(\"#CLOSE\", NULL, id);\n        if (CloseButton(close_button_id, ImVec2(button_x, button_y)))\n            *p_visible = false;\n        g.LastItemData = last_item_backup;\n    }\n\n    return is_open;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Selectable\n//-------------------------------------------------------------------------\n// - Selectable()\n//-------------------------------------------------------------------------\n\n// Tip: pass a non-visible label (e.g. \"##hello\") then you can use the space to draw other text or image.\n// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id.\n// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowOverlap are also frequently used flags.\n// FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported.\nbool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n\n    // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.\n    ImGuiID id = window->GetID(label);\n    ImVec2 label_size = CalcTextSize(label, NULL, true);\n    ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);\n    ImVec2 pos = window->DC.CursorPos;\n    pos.y += window->DC.CurrLineTextBaseOffset;\n    ItemSize(size, 0.0f);\n\n    // Fill horizontal space\n    // We don't support (size < 0.0f) in Selectable() because the ItemSpacing extension would make explicitly right-aligned sizes not visibly match other widgets.\n    const bool span_all_columns = (flags & ImGuiSelectableFlags_SpanAllColumns) != 0;\n    const float min_x = span_all_columns ? window->ParentWorkRect.Min.x : pos.x;\n    const float max_x = span_all_columns ? window->ParentWorkRect.Max.x : window->WorkRect.Max.x;\n    if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth))\n        size.x = ImMax(label_size.x, max_x - min_x);\n\n    // Text stays at the submission position, but bounding box may be extended on both sides\n    const ImVec2 text_min = pos;\n    const ImVec2 text_max(min_x + size.x, pos.y + size.y);\n\n    // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable.\n    ImRect bb(min_x, pos.y, text_max.x, text_max.y);\n    if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0)\n    {\n        const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x;\n        const float spacing_y = style.ItemSpacing.y;\n        const float spacing_L = IM_TRUNC(spacing_x * 0.50f);\n        const float spacing_U = IM_TRUNC(spacing_y * 0.50f);\n        bb.Min.x -= spacing_L;\n        bb.Min.y -= spacing_U;\n        bb.Max.x += (spacing_x - spacing_L);\n        bb.Max.y += (spacing_y - spacing_U);\n    }\n    //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); }\n\n    // Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..\n    const float backup_clip_rect_min_x = window->ClipRect.Min.x;\n    const float backup_clip_rect_max_x = window->ClipRect.Max.x;\n    if (span_all_columns)\n    {\n        window->ClipRect.Min.x = window->ParentWorkRect.Min.x;\n        window->ClipRect.Max.x = window->ParentWorkRect.Max.x;\n    }\n\n    const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;\n    const bool item_add = ItemAdd(bb, id, NULL, disabled_item ? ImGuiItemFlags_Disabled : ImGuiItemFlags_None);\n    if (span_all_columns)\n    {\n        window->ClipRect.Min.x = backup_clip_rect_min_x;\n        window->ClipRect.Max.x = backup_clip_rect_max_x;\n    }\n\n    if (!item_add)\n        return false;\n\n    const bool disabled_global = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0;\n    if (disabled_item && !disabled_global) // Only testing this as an optimization\n        BeginDisabled();\n\n    // FIXME: We can standardize the behavior of those two, we could also keep the fast path of override ClipRect + full push on render only,\n    // which would be advantageous since most selectable are not selected.\n    if (span_all_columns && window->DC.CurrentColumns)\n        PushColumnsBackground();\n    else if (span_all_columns && g.CurrentTable)\n        TablePushBackgroundChannel();\n\n    // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries\n    ImGuiButtonFlags button_flags = 0;\n    if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; }\n    if (flags & ImGuiSelectableFlags_NoSetKeyOwner)     { button_flags |= ImGuiButtonFlags_NoSetKeyOwner; }\n    if (flags & ImGuiSelectableFlags_SelectOnClick)     { button_flags |= ImGuiButtonFlags_PressedOnClick; }\n    if (flags & ImGuiSelectableFlags_SelectOnRelease)   { button_flags |= ImGuiButtonFlags_PressedOnRelease; }\n    if (flags & ImGuiSelectableFlags_AllowDoubleClick)  { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; }\n    if ((flags & ImGuiSelectableFlags_AllowOverlap) || (g.LastItemData.InFlags & ImGuiItemFlags_AllowOverlap)) { button_flags |= ImGuiButtonFlags_AllowOverlap; }\n\n    const bool was_selected = selected;\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);\n\n    // Auto-select when moved into\n    // - This will be more fully fleshed in the range-select branch\n    // - This is not exposed as it won't nicely work with some user side handling of shift/control\n    // - We cannot do 'if (g.NavJustMovedToId != id) { selected = false; pressed = was_selected; }' for two reasons\n    //   - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope())\n    //   - (2) usage will fail with clipped items\n    //   The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API.\n    if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId)\n        if (g.NavJustMovedToId == id)\n            selected = pressed = true;\n\n    // Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard\n    if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))\n    {\n        if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)\n        {\n            SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect)\n            g.NavDisableHighlight = true;\n        }\n    }\n    if (pressed)\n        MarkItemEdited(id);\n\n    // In this branch, Selectable() cannot toggle the selection so this will never trigger.\n    if (selected != was_selected) //-V547\n        g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;\n\n    // Render\n    if (hovered || selected)\n    {\n        const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);\n        RenderFrame(bb.Min, bb.Max, col, false, 0.0f);\n    }\n    if (g.NavId == id)\n        RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);\n\n    if (span_all_columns && window->DC.CurrentColumns)\n        PopColumnsBackground();\n    else if (span_all_columns && g.CurrentTable)\n        TablePopBackgroundChannel();\n\n    RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb);\n\n    // Automatically close popups\n    if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(g.LastItemData.InFlags & ImGuiItemFlags_SelectableDontClosePopup))\n        CloseCurrentPopup();\n\n    if (disabled_item && !disabled_global)\n        EndDisabled();\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n    return pressed; //-V1020\n}\n\nbool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)\n{\n    if (Selectable(label, *p_selected, flags, size_arg))\n    {\n        *p_selected = !*p_selected;\n        return true;\n    }\n    return false;\n}\n\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Typing-Select support\n//-------------------------------------------------------------------------\n\n// [Experimental] Currently not exposed in public API.\n// Consume character inputs and return search request, if any.\n// This would typically only be called on the focused window or location you want to grab inputs for, e.g.\n//   if (ImGui::IsWindowFocused(...))\n//       if (ImGuiTypingSelectRequest* req = ImGui::GetTypingSelectRequest())\n//           focus_idx = ImGui::TypingSelectFindMatch(req, my_items.size(), [](void*, int n) { return my_items[n]->Name; }, &my_items, -1);\n// However the code is written in a way where calling it from multiple locations is safe (e.g. to obtain buffer).\nImGuiTypingSelectRequest* ImGui::GetTypingSelectRequest(ImGuiTypingSelectFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiTypingSelectState* data = &g.TypingSelectState;\n    ImGuiTypingSelectRequest* out_request = &data->Request;\n\n    // Clear buffer\n    const float TYPING_SELECT_RESET_TIMER = 1.80f;          // FIXME: Potentially move to IO config.\n    const int TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK = 4; // Lock single char matching when repeating same char 4 times\n    if (data->SearchBuffer[0] != 0)\n    {\n        bool clear_buffer = false;\n        clear_buffer |= (g.NavFocusScopeId != data->FocusScope);\n        clear_buffer |= (data->LastRequestTime + TYPING_SELECT_RESET_TIMER < g.Time);\n        clear_buffer |= g.NavAnyRequest;\n        clear_buffer |= g.ActiveId != 0 && g.NavActivateId == 0; // Allow temporary SPACE activation to not interfere\n        clear_buffer |= IsKeyPressed(ImGuiKey_Escape) || IsKeyPressed(ImGuiKey_Enter);\n        clear_buffer |= IsKeyPressed(ImGuiKey_Backspace) && (flags & ImGuiTypingSelectFlags_AllowBackspace) == 0;\n        //if (clear_buffer) { IMGUI_DEBUG_LOG(\"GetTypingSelectRequest(): Clear SearchBuffer.\\n\"); }\n        if (clear_buffer)\n            data->Clear();\n    }\n\n    // Append to buffer\n    const int buffer_max_len = IM_ARRAYSIZE(data->SearchBuffer) - 1;\n    int buffer_len = (int)strlen(data->SearchBuffer);\n    bool select_request = false;\n    for (ImWchar w : g.IO.InputQueueCharacters)\n    {\n        const int w_len = ImTextCountUtf8BytesFromStr(&w, &w + 1);\n        if (w < 32 || (buffer_len == 0 && ImCharIsBlankW(w)) || (buffer_len + w_len > buffer_max_len)) // Ignore leading blanks\n            continue;\n        char w_buf[5];\n        ImTextCharToUtf8(w_buf, (unsigned int)w);\n        if (data->SingleCharModeLock && w_len == out_request->SingleCharSize && memcmp(w_buf, data->SearchBuffer, w_len) == 0)\n        {\n            select_request = true; // Same character: don't need to append to buffer.\n            continue;\n        }\n        if (data->SingleCharModeLock)\n        {\n            data->Clear(); // Different character: clear\n            buffer_len = 0;\n        }\n        memcpy(data->SearchBuffer + buffer_len, w_buf, w_len + 1); // Append\n        buffer_len += w_len;\n        select_request = true;\n    }\n    g.IO.InputQueueCharacters.resize(0);\n\n    // Handle backspace\n    if ((flags & ImGuiTypingSelectFlags_AllowBackspace) && IsKeyPressed(ImGuiKey_Backspace, 0, ImGuiInputFlags_Repeat))\n    {\n        char* p = (char*)(void*)ImTextFindPreviousUtf8Codepoint(data->SearchBuffer, data->SearchBuffer + buffer_len);\n        *p = 0;\n        buffer_len = (int)(p - data->SearchBuffer);\n    }\n\n    // Return request if any\n    if (buffer_len == 0)\n        return NULL;\n    if (select_request)\n    {\n        data->FocusScope = g.NavFocusScopeId;\n        data->LastRequestFrame = g.FrameCount;\n        data->LastRequestTime = (float)g.Time;\n    }\n    out_request->Flags = flags;\n    out_request->SearchBufferLen = buffer_len;\n    out_request->SearchBuffer = data->SearchBuffer;\n    out_request->SelectRequest = (data->LastRequestFrame == g.FrameCount);\n    out_request->SingleCharMode = false;\n    out_request->SingleCharSize = 0;\n\n    // Calculate if buffer contains the same character repeated.\n    // - This can be used to implement a special search mode on first character.\n    // - Performed on UTF-8 codepoint for correctness.\n    // - SingleCharMode is always set for first input character, because it usually leads to a \"next\".\n    if (flags & ImGuiTypingSelectFlags_AllowSingleCharMode)\n    {\n        const char* buf_begin = out_request->SearchBuffer;\n        const char* buf_end = out_request->SearchBuffer + out_request->SearchBufferLen;\n        const int c0_len = ImTextCountUtf8BytesFromChar(buf_begin, buf_end);\n        const char* p = buf_begin + c0_len;\n        for (; p < buf_end; p += c0_len)\n            if (memcmp(buf_begin, p, (size_t)c0_len) != 0)\n                break;\n        const int single_char_count = (p == buf_end) ? (out_request->SearchBufferLen / c0_len) : 0;\n        out_request->SingleCharMode = (single_char_count > 0 || data->SingleCharModeLock);\n        out_request->SingleCharSize = (ImS8)c0_len;\n        data->SingleCharModeLock |= (single_char_count >= TYPING_SELECT_SINGLE_CHAR_COUNT_FOR_LOCK); // From now on we stop search matching to lock to single char mode.\n    }\n\n    return out_request;\n}\n\nstatic int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2)\n{\n    int match_len = 0;\n    while (s1 < s1_end && ImToUpper(*s1++) == ImToUpper(*s2++))\n        match_len++;\n    return match_len;\n}\n\n// Default handler for finding a result for typing-select. You may implement your own.\n// You might want to display a tooltip to visualize the current request SearchBuffer\n// When SingleCharMode is set:\n// - it is better to NOT display a tooltip of other on-screen display indicator.\n// - the index of the currently focused item is required.\n//   if your SetNextItemSelectionData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData.\nint ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx)\n{\n    if (req == NULL || req->SelectRequest == false) // Support NULL parameter so both calls can be done from same spot.\n        return -1;\n    int idx = -1;\n    if (req->SingleCharMode && (req->Flags & ImGuiTypingSelectFlags_AllowSingleCharMode))\n        idx = TypingSelectFindNextSingleCharMatch(req, items_count, get_item_name_func, user_data, nav_item_idx);\n    else\n        idx = TypingSelectFindBestLeadingMatch(req, items_count, get_item_name_func, user_data);\n    if (idx != -1)\n        NavRestoreHighlightAfterMove();\n    return idx;\n}\n\n// Special handling when a single character is repeated: perform search on a single letter and goes to next.\nint ImGui::TypingSelectFindNextSingleCharMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx)\n{\n    // FIXME: Assume selection user data is index. Would be extremely practical.\n    //if (nav_item_idx == -1)\n    //    nav_item_idx = (int)g.NavLastValidSelectionUserData;\n\n    int first_match_idx = -1;\n    bool return_next_match = false;\n    for (int idx = 0; idx < items_count; idx++)\n    {\n        const char* item_name = get_item_name_func(user_data, idx);\n        if (ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SingleCharSize, item_name) < req->SingleCharSize)\n            continue;\n        if (return_next_match)                           // Return next matching item after current item.\n            return idx;\n        if (first_match_idx == -1 && nav_item_idx == -1) // Return first match immediately if we don't have a nav_item_idx value.\n            return idx;\n        if (first_match_idx == -1)                       // Record first match for wrapping.\n            first_match_idx = idx;\n        if (nav_item_idx == idx)                         // Record that we encountering nav_item so we can return next match.\n            return_next_match = true;\n    }\n    return first_match_idx; // First result\n}\n\nint ImGui::TypingSelectFindBestLeadingMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data)\n{\n    int longest_match_idx = -1;\n    int longest_match_len = 0;\n    for (int idx = 0; idx < items_count; idx++)\n    {\n        const char* item_name = get_item_name_func(user_data, idx);\n        const int match_len = ImStrimatchlen(req->SearchBuffer, req->SearchBuffer + req->SearchBufferLen, item_name);\n        if (match_len <= longest_match_len)\n            continue;\n        longest_match_idx = idx;\n        longest_match_len = match_len;\n        if (match_len == req->SearchBufferLen)\n            break;\n    }\n    return longest_match_idx;\n}\n\nvoid ImGui::DebugNodeTypingSelectState(ImGuiTypingSelectState* data)\n{\n#ifndef IMGUI_DISABLE_DEBUG_TOOLS\n    Text(\"SearchBuffer = \\\"%s\\\"\", data->SearchBuffer);\n    Text(\"SingleCharMode = %d, Size = %d, Lock = %d\", data->Request.SingleCharMode, data->Request.SingleCharSize, data->SingleCharModeLock);\n    Text(\"LastRequest = time: %.2f, frame: %d\", data->LastRequestTime, data->LastRequestFrame);\n#else\n    IM_UNUSED(data);\n#endif\n}\n\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Multi-Select support\n//-------------------------------------------------------------------------\n\nvoid ImGui::SetNextItemSelectionUserData(ImGuiSelectionUserData selection_user_data)\n{\n    // Note that flags will be cleared by ItemAdd(), so it's only useful for Navigation code!\n    // This designed so widgets can also cheaply set this before calling ItemAdd(), so we are not tied to MultiSelect api.\n    ImGuiContext& g = *GImGui;\n    g.NextItemData.ItemFlags |= ImGuiItemFlags_HasSelectionUserData;\n    g.NextItemData.SelectionUserData = selection_user_data;\n}\n\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: ListBox\n//-------------------------------------------------------------------------\n// - BeginListBox()\n// - EndListBox()\n// - ListBox()\n//-------------------------------------------------------------------------\n\n// This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label.\n// Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. \"##empty\"\n// Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height).\nbool ImGui::BeginListBox(const char* label, const ImVec2& size_arg)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = GetID(label);\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    // Size default to hold ~7.25 items.\n    // Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar.\n    ImVec2 size = ImTrunc(CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.25f + style.FramePadding.y * 2.0f));\n    ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y));\n    ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);\n    ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f));\n    g.NextItemData.ClearFlags();\n\n    if (!IsRectVisible(bb.Min, bb.Max))\n    {\n        ItemSize(bb.GetSize(), style.FramePadding.y);\n        ItemAdd(bb, 0, &frame_bb);\n        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n        return false;\n    }\n\n    // FIXME-OPT: We could omit the BeginGroup() if label_size.x == 0.0f but would need to omit the EndGroup() as well.\n    BeginGroup();\n    if (label_size.x > 0.0f)\n    {\n        ImVec2 label_pos = ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y);\n        RenderText(label_pos, label);\n        window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, label_pos + label_size);\n    }\n\n    BeginChild(id, frame_bb.GetSize(), ImGuiChildFlags_FrameStyle);\n    return true;\n}\n\nvoid ImGui::EndListBox()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) && \"Mismatched BeginListBox/EndListBox calls. Did you test the return value of BeginListBox?\");\n    IM_UNUSED(window);\n\n    EndChild();\n    EndGroup(); // This is only required to be able to do IsItemXXX query on the whole ListBox including label\n}\n\nbool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items)\n{\n    const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items);\n    return value_changed;\n}\n\n// This is merely a helper around BeginListBox(), EndListBox().\n// Considering using those directly to submit custom data or store selection differently.\nbool ImGui::ListBox(const char* label, int* current_item, const char* (*getter)(void* user_data, int idx), void* user_data, int items_count, int height_in_items)\n{\n    ImGuiContext& g = *GImGui;\n\n    // Calculate size from \"height_in_items\"\n    if (height_in_items < 0)\n        height_in_items = ImMin(items_count, 7);\n    float height_in_items_f = height_in_items + 0.25f;\n    ImVec2 size(0.0f, ImTrunc(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f));\n\n    if (!BeginListBox(label, size))\n        return false;\n\n    // Assume all items have even height (= 1 line of text). If you need items of different height,\n    // you can create a custom version of ListBox() in your code without using the clipper.\n    bool value_changed = false;\n    ImGuiListClipper clipper;\n    clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.\n    while (clipper.Step())\n        for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)\n        {\n            const char* item_text = getter(user_data, i);\n            if (item_text == NULL)\n                item_text = \"*Unknown item*\";\n\n            PushID(i);\n            const bool item_selected = (i == *current_item);\n            if (Selectable(item_text, item_selected))\n            {\n                *current_item = i;\n                value_changed = true;\n            }\n            if (item_selected)\n                SetItemDefaultFocus();\n            PopID();\n        }\n    EndListBox();\n\n    if (value_changed)\n        MarkItemEdited(g.LastItemData.ID);\n\n    return value_changed;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: PlotLines, PlotHistogram\n//-------------------------------------------------------------------------\n// - PlotEx() [Internal]\n// - PlotLines()\n// - PlotHistogram()\n//-------------------------------------------------------------------------\n// Plot/Graph widgets are not very good.\n// Consider writing your own, or using a third-party one, see:\n// - ImPlot https://github.com/epezent/implot\n// - others https://github.com/ocornut/imgui/wiki/Useful-Extensions\n//-------------------------------------------------------------------------\n\nint ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, const ImVec2& size_arg)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return -1;\n\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n\n    const ImVec2 label_size = CalcTextSize(label, NULL, true);\n    const ImVec2 frame_size = CalcItemSize(size_arg, CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f);\n\n    const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);\n    const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding);\n    const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0));\n    ItemSize(total_bb, style.FramePadding.y);\n    if (!ItemAdd(total_bb, 0, &frame_bb))\n        return -1;\n    const bool hovered = ItemHoverable(frame_bb, id, g.LastItemData.InFlags);\n\n    // Determine scale from values if not specified\n    if (scale_min == FLT_MAX || scale_max == FLT_MAX)\n    {\n        float v_min = FLT_MAX;\n        float v_max = -FLT_MAX;\n        for (int i = 0; i < values_count; i++)\n        {\n            const float v = values_getter(data, i);\n            if (v != v) // Ignore NaN values\n                continue;\n            v_min = ImMin(v_min, v);\n            v_max = ImMax(v_max, v);\n        }\n        if (scale_min == FLT_MAX)\n            scale_min = v_min;\n        if (scale_max == FLT_MAX)\n            scale_max = v_max;\n    }\n\n    RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);\n\n    const int values_count_min = (plot_type == ImGuiPlotType_Lines) ? 2 : 1;\n    int idx_hovered = -1;\n    if (values_count >= values_count_min)\n    {\n        int res_w = ImMin((int)frame_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);\n        int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0);\n\n        // Tooltip on hover\n        if (hovered && inner_bb.Contains(g.IO.MousePos))\n        {\n            const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f);\n            const int v_idx = (int)(t * item_count);\n            IM_ASSERT(v_idx >= 0 && v_idx < values_count);\n\n            const float v0 = values_getter(data, (v_idx + values_offset) % values_count);\n            const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count);\n            if (plot_type == ImGuiPlotType_Lines)\n                SetTooltip(\"%d: %8.4g\\n%d: %8.4g\", v_idx, v0, v_idx + 1, v1);\n            else if (plot_type == ImGuiPlotType_Histogram)\n                SetTooltip(\"%d: %8.4g\", v_idx, v0);\n            idx_hovered = v_idx;\n        }\n\n        const float t_step = 1.0f / (float)res_w;\n        const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min));\n\n        float v0 = values_getter(data, (0 + values_offset) % values_count);\n        float t0 = 0.0f;\n        ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) );                       // Point in the normalized space of our target rectangle\n        float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (1 + scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f);   // Where does the zero line stands\n\n        const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram);\n        const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered);\n\n        for (int n = 0; n < res_w; n++)\n        {\n            const float t1 = t0 + t_step;\n            const int v1_idx = (int)(t0 * item_count + 0.5f);\n            IM_ASSERT(v1_idx >= 0 && v1_idx < values_count);\n            const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count);\n            const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) );\n\n            // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU.\n            ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0);\n            ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t));\n            if (plot_type == ImGuiPlotType_Lines)\n            {\n                window->DrawList->AddLine(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);\n            }\n            else if (plot_type == ImGuiPlotType_Histogram)\n            {\n                if (pos1.x >= pos0.x + 2.0f)\n                    pos1.x -= 1.0f;\n                window->DrawList->AddRectFilled(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);\n            }\n\n            t0 = t1;\n            tp0 = tp1;\n        }\n    }\n\n    // Text overlay\n    if (overlay_text)\n        RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f, 0.0f));\n\n    if (label_size.x > 0.0f)\n        RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label);\n\n    // Return hovered index or -1 if none are hovered.\n    // This is currently not exposed in the public API because we need a larger redesign of the whole thing, but in the short-term we are making it available in PlotEx().\n    return idx_hovered;\n}\n\nstruct ImGuiPlotArrayGetterData\n{\n    const float* Values;\n    int Stride;\n\n    ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; }\n};\n\nstatic float Plot_ArrayGetter(void* data, int idx)\n{\n    ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data;\n    const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride);\n    return v;\n}\n\nvoid ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)\n{\n    ImGuiPlotArrayGetterData data(values, stride);\n    PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);\n}\n\nvoid ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)\n{\n    PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);\n}\n\nvoid ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride)\n{\n    ImGuiPlotArrayGetterData data(values, stride);\n    PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);\n}\n\nvoid ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size)\n{\n    PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: Value helpers\n// Those is not very useful, legacy API.\n//-------------------------------------------------------------------------\n// - Value()\n//-------------------------------------------------------------------------\n\nvoid ImGui::Value(const char* prefix, bool b)\n{\n    Text(\"%s: %s\", prefix, (b ? \"true\" : \"false\"));\n}\n\nvoid ImGui::Value(const char* prefix, int v)\n{\n    Text(\"%s: %d\", prefix, v);\n}\n\nvoid ImGui::Value(const char* prefix, unsigned int v)\n{\n    Text(\"%s: %d\", prefix, v);\n}\n\nvoid ImGui::Value(const char* prefix, float v, const char* float_format)\n{\n    if (float_format)\n    {\n        char fmt[64];\n        ImFormatString(fmt, IM_ARRAYSIZE(fmt), \"%%s: %s\", float_format);\n        Text(fmt, prefix, v);\n    }\n    else\n    {\n        Text(\"%s: %.3f\", prefix, v);\n    }\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] MenuItem, BeginMenu, EndMenu, etc.\n//-------------------------------------------------------------------------\n// - ImGuiMenuColumns [Internal]\n// - BeginMenuBar()\n// - EndMenuBar()\n// - BeginMainMenuBar()\n// - EndMainMenuBar()\n// - BeginMenu()\n// - EndMenu()\n// - MenuItemEx() [Internal]\n// - MenuItem()\n//-------------------------------------------------------------------------\n\n// Helpers for internal use\nvoid ImGuiMenuColumns::Update(float spacing, bool window_reappearing)\n{\n    if (window_reappearing)\n        memset(Widths, 0, sizeof(Widths));\n    Spacing = (ImU16)spacing;\n    CalcNextTotalWidth(true);\n    memset(Widths, 0, sizeof(Widths));\n    TotalWidth = NextTotalWidth;\n    NextTotalWidth = 0;\n}\n\nvoid ImGuiMenuColumns::CalcNextTotalWidth(bool update_offsets)\n{\n    ImU16 offset = 0;\n    bool want_spacing = false;\n    for (int i = 0; i < IM_ARRAYSIZE(Widths); i++)\n    {\n        ImU16 width = Widths[i];\n        if (want_spacing && width > 0)\n            offset += Spacing;\n        want_spacing |= (width > 0);\n        if (update_offsets)\n        {\n            if (i == 1) { OffsetLabel = offset; }\n            if (i == 2) { OffsetShortcut = offset; }\n            if (i == 3) { OffsetMark = offset; }\n        }\n        offset += width;\n    }\n    NextTotalWidth = offset;\n}\n\nfloat ImGuiMenuColumns::DeclColumns(float w_icon, float w_label, float w_shortcut, float w_mark)\n{\n    Widths[0] = ImMax(Widths[0], (ImU16)w_icon);\n    Widths[1] = ImMax(Widths[1], (ImU16)w_label);\n    Widths[2] = ImMax(Widths[2], (ImU16)w_shortcut);\n    Widths[3] = ImMax(Widths[3], (ImU16)w_mark);\n    CalcNextTotalWidth(false);\n    return (float)ImMax(TotalWidth, NextTotalWidth);\n}\n\n// FIXME: Provided a rectangle perhaps e.g. a BeginMenuBarEx() could be used anywhere..\n// Currently the main responsibility of this function being to setup clip-rect + horizontal layout + menu navigation layer.\n// Ideally we also want this to be responsible for claiming space out of the main window scrolling rectangle, in which case ImGuiWindowFlags_MenuBar will become unnecessary.\n// Then later the same system could be used for multiple menu-bars, scrollbars, side-bars.\nbool ImGui::BeginMenuBar()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n    if (!(window->Flags & ImGuiWindowFlags_MenuBar))\n        return false;\n\n    IM_ASSERT(!window->DC.MenuBarAppending);\n    BeginGroup(); // Backup position on layer 0 // FIXME: Misleading to use a group for that backup/restore\n    PushID(\"##menubar\");\n\n    // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect.\n    // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy.\n    ImRect bar_rect = window->MenuBarRect();\n    ImRect clip_rect(IM_ROUND(bar_rect.Min.x + window->WindowBorderSize), IM_ROUND(bar_rect.Min.y + window->WindowBorderSize), IM_ROUND(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, window->WindowBorderSize))), IM_ROUND(bar_rect.Max.y));\n    clip_rect.ClipWith(window->OuterRectClipped);\n    PushClipRect(clip_rect.Min, clip_rect.Max, false);\n\n    // We overwrite CursorMaxPos because BeginGroup sets it to CursorPos (essentially the .EmitItem hack in EndMenuBar() would need something analogous here, maybe a BeginGroupEx() with flags).\n    window->DC.CursorPos = window->DC.CursorMaxPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y);\n    window->DC.LayoutType = ImGuiLayoutType_Horizontal;\n    window->DC.IsSameLine = false;\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Menu;\n    window->DC.MenuBarAppending = true;\n    AlignTextToFramePadding();\n    return true;\n}\n\nvoid ImGui::EndMenuBar()\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return;\n    ImGuiContext& g = *GImGui;\n\n    // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings.\n    if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu))\n    {\n        // Try to find out if the request is for one of our child menu\n        ImGuiWindow* nav_earliest_child = g.NavWindow;\n        while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu))\n            nav_earliest_child = nav_earliest_child->ParentWindow;\n        if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded) == 0)\n        {\n            // To do so we claim focus back, restore NavId and then process the movement request for yet another frame.\n            // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering)\n            const ImGuiNavLayer layer = ImGuiNavLayer_Menu;\n            IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check (FIXME: Seems unnecessary)\n            FocusWindow(window);\n            SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]);\n            g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection.\n            g.NavDisableMouseHover = g.NavMousePosDirty = true;\n            NavMoveRequestForward(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); // Repeat\n        }\n    }\n\n    IM_MSVC_WARNING_SUPPRESS(6011); // Static Analysis false positive \"warning C6011: Dereferencing NULL pointer 'window'\"\n    IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar);\n    IM_ASSERT(window->DC.MenuBarAppending);\n    PopClipRect();\n    PopID();\n    window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->Pos.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos.\n\n    // FIXME: Extremely confusing, cleanup by (a) working on WorkRect stack system (b) not using a Group confusingly here.\n    ImGuiGroupData& group_data = g.GroupStack.back();\n    group_data.EmitItem = false;\n    ImVec2 restore_cursor_max_pos = group_data.BackupCursorMaxPos;\n    window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, window->DC.CursorMaxPos.x - window->Scroll.x); // Convert ideal extents for scrolling layer equivalent.\n    EndGroup(); // Restore position on layer 0 // FIXME: Misleading to use a group for that backup/restore\n    window->DC.LayoutType = ImGuiLayoutType_Vertical;\n    window->DC.IsSameLine = false;\n    window->DC.NavLayerCurrent = ImGuiNavLayer_Main;\n    window->DC.MenuBarAppending = false;\n    window->DC.CursorMaxPos = restore_cursor_max_pos;\n}\n\n// Important: calling order matters!\n// FIXME: Somehow overlapping with docking tech.\n// FIXME: The \"rect-cut\" aspect of this could be formalized into a lower-level helper (rect-cut: https://halt.software/dead-simple-layouts)\nbool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, ImGuiDir dir, float axis_size, ImGuiWindowFlags window_flags)\n{\n    IM_ASSERT(dir != ImGuiDir_None);\n\n    ImGuiWindow* bar_window = FindWindowByName(name);\n    if (bar_window == NULL || bar_window->BeginCount == 0)\n    {\n        // Calculate and set window size/position\n        ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport());\n        ImRect avail_rect = viewport->GetBuildWorkRect();\n        ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X;\n        ImVec2 pos = avail_rect.Min;\n        if (dir == ImGuiDir_Right || dir == ImGuiDir_Down)\n            pos[axis] = avail_rect.Max[axis] - axis_size;\n        ImVec2 size = avail_rect.GetSize();\n        size[axis] = axis_size;\n        SetNextWindowPos(pos);\n        SetNextWindowSize(size);\n\n        // Report our size into work area (for next frame) using actual window size\n        if (dir == ImGuiDir_Up || dir == ImGuiDir_Left)\n            viewport->BuildWorkOffsetMin[axis] += axis_size;\n        else if (dir == ImGuiDir_Down || dir == ImGuiDir_Right)\n            viewport->BuildWorkOffsetMax[axis] -= axis_size;\n    }\n\n    window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;\n    PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);\n    PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint\n    bool is_open = Begin(name, NULL, window_flags);\n    PopStyleVar(2);\n\n    return is_open;\n}\n\nbool ImGui::BeginMainMenuBar()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();\n\n    // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.\n    // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea?\n    // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings.\n    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));\n    ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;\n    float height = GetFrameHeight();\n    bool is_open = BeginViewportSideBar(\"##MainMenuBar\", viewport, ImGuiDir_Up, height, window_flags);\n    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);\n\n    if (is_open)\n        BeginMenuBar();\n    else\n        End();\n    return is_open;\n}\n\nvoid ImGui::EndMainMenuBar()\n{\n    EndMenuBar();\n\n    // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window\n    // FIXME: With this strategy we won't be able to restore a NULL focus.\n    ImGuiContext& g = *GImGui;\n    if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest)\n        FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild);\n\n    End();\n}\n\nstatic bool IsRootOfOpenMenuSet()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if ((g.OpenPopupStack.Size <= g.BeginPopupStack.Size) || (window->Flags & ImGuiWindowFlags_ChildMenu))\n        return false;\n\n    // Initially we used 'upper_popup->OpenParentId == window->IDStack.back()' to differentiate multiple menu sets from each others\n    // (e.g. inside menu bar vs loose menu items) based on parent ID.\n    // This would however prevent the use of e.g. PushID() user code submitting menus.\n    // Previously this worked between popup and a first child menu because the first child menu always had the _ChildWindow flag,\n    // making hovering on parent popup possible while first child menu was focused - but this was generally a bug with other side effects.\n    // Instead we don't treat Popup specifically (in order to consistently support menu features in them), maybe the first child menu of a Popup\n    // doesn't have the _ChildWindow flag, and we rely on this IsRootOfOpenMenuSet() check to allow hovering between root window/popup and first child menu.\n    // In the end, lack of ID check made it so we could no longer differentiate between separate menu sets. To compensate for that, we at least check parent window nav layer.\n    // This fixes the most common case of menu opening on hover when moving between window content and menu bar. Multiple different menu sets in same nav layer would still\n    // open on hover, but that should be a lesser problem, because if such menus are close in proximity in window content then it won't feel weird and if they are far apart\n    // it likely won't be a problem anyone runs into.\n    const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size];\n    if (window->DC.NavLayerCurrent != upper_popup->ParentNavLayer)\n        return false;\n    return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window, true);\n}\n\nbool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = window->GetID(label);\n    bool menu_is_open = IsPopupOpen(id, ImGuiPopupFlags_None);\n\n    // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu)\n    // The first menu in a hierarchy isn't so hovering doesn't get across (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation.\n    ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus;\n    if (window->Flags & ImGuiWindowFlags_ChildMenu)\n        window_flags |= ImGuiWindowFlags_ChildWindow;\n\n    // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin().\n    // We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame.\n    // If somehow this is ever becoming a problem we can switch to use e.g. ImGuiStorage mapping key to last frame used.\n    if (g.MenusIdSubmittedThisFrame.contains(id))\n    {\n        if (menu_is_open)\n            menu_is_open = BeginPopupEx(id, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)\n        else\n            g.NextWindowData.ClearFlags();          // we behave like Begin() and need to consume those values\n        return menu_is_open;\n    }\n\n    // Tag menu as used. Next time BeginMenu() with same ID is called it will append to existing menu\n    g.MenusIdSubmittedThisFrame.push_back(id);\n\n    ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window)\n    // This is only done for items for the menu set and not the full parent window.\n    const bool menuset_is_open = IsRootOfOpenMenuSet();\n    if (menuset_is_open)\n        PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);\n\n    // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu,\n    // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup().\n    // e.g. Menus tend to overlap each other horizontally to amplify relative Z-ordering.\n    ImVec2 popup_pos, pos = window->DC.CursorPos;\n    PushID(label);\n    if (!enabled)\n        BeginDisabled();\n    const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;\n    bool pressed;\n\n    // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another.\n    const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups;\n    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)\n    {\n        // Menu inside an horizontal menu bar\n        // Selectable extend their highlight by half ItemSpacing in each direction.\n        // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin()\n        popup_pos = ImVec2(pos.x - 1.0f - IM_TRUNC(style.ItemSpacing.x * 0.5f), pos.y - style.FramePadding.y + window->MenuBarHeight());\n        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);\n        PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));\n        float w = label_size.x;\n        ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);\n        pressed = Selectable(\"\", menu_is_open, selectable_flags, ImVec2(w, label_size.y));\n        RenderText(text_pos, label);\n        PopStyleVar();\n        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().\n    }\n    else\n    {\n        // Menu inside a regular/vertical menu\n        // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.\n        //  Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.\n        popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);\n        float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;\n        float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);\n        float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, 0.0f, checkmark_w); // Feedback to next frame\n        float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);\n        ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);\n        pressed = Selectable(\"\", menu_is_open, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));\n        RenderText(text_pos, label);\n        if (icon_w > 0.0f)\n            RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);\n        RenderArrow(window->DrawList, pos + ImVec2(offsets->OffsetMark + extra_w + g.FontSize * 0.30f, 0.0f), GetColorU32(ImGuiCol_Text), ImGuiDir_Right);\n    }\n    if (!enabled)\n        EndDisabled();\n\n    const bool hovered = (g.HoveredId == id) && enabled && !g.NavDisableMouseHover;\n    if (menuset_is_open)\n        PopItemFlag();\n\n    bool want_open = false;\n    bool want_close = false;\n    if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu))\n    {\n        // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu\n        // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive.\n        bool moving_toward_child_menu = false;\n        ImGuiPopupData* child_popup = (g.BeginPopupStack.Size < g.OpenPopupStack.Size) ? &g.OpenPopupStack[g.BeginPopupStack.Size] : NULL; // Popup candidate (testing below)\n        ImGuiWindow* child_menu_window = (child_popup && child_popup->Window && child_popup->Window->ParentWindow == window) ? child_popup->Window : NULL;\n        if (g.HoveredWindow == window && child_menu_window != NULL)\n        {\n            float ref_unit = g.FontSize; // FIXME-DPI\n            float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f;\n            ImRect next_window_rect = child_menu_window->Rect();\n            ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta);\n            ImVec2 tb = (child_dir > 0.0f) ? next_window_rect.GetTL() : next_window_rect.GetTR();\n            ImVec2 tc = (child_dir > 0.0f) ? next_window_rect.GetBL() : next_window_rect.GetBR();\n            float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f);   // add a bit of extra slack.\n            ta.x += child_dir * -0.5f;\n            tb.x += child_dir * ref_unit;\n            tc.x += child_dir * ref_unit;\n            tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f);                           // triangle has maximum height to limit the slope and the bias toward large sub-menus\n            tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f);\n            moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos);\n            //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG]\n        }\n\n        // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not)\n        // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon.\n        // (Remember to test this on BeginPopup(\"A\")->BeginMenu(\"B\") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.)\n        if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavDisableMouseHover && g.ActiveId == 0)\n            want_close = true;\n\n        // Open\n        if (!menu_is_open && pressed) // Click/activate to open\n            want_open = true;\n        else if (!menu_is_open && hovered && !moving_toward_child_menu) // Hover to open\n            want_open = true;\n        if (g.NavId == id && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open\n        {\n            want_open = true;\n            NavMoveRequestCancel();\n        }\n    }\n    else\n    {\n        // Menu bar\n        if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it\n        {\n            want_close = true;\n            want_open = menu_is_open = false;\n        }\n        else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others\n        {\n            want_open = true;\n        }\n        else if (g.NavId == id && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open\n        {\n            want_open = true;\n            NavMoveRequestCancel();\n        }\n    }\n\n    if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu(\"options\", has_object)) { ..use object.. }'\n        want_close = true;\n    if (want_close && IsPopupOpen(id, ImGuiPopupFlags_None))\n        ClosePopupToLevel(g.BeginPopupStack.Size, true);\n\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0));\n    PopID();\n\n    if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size)\n    {\n        // Don't reopen/recycle same menu level in the same frame, first close the other menu and yield for a frame.\n        OpenPopup(label);\n    }\n    else if (want_open)\n    {\n        menu_is_open = true;\n        OpenPopup(label);\n    }\n\n    if (menu_is_open)\n    {\n        ImGuiLastItemData last_item_in_parent = g.LastItemData;\n        SetNextWindowPos(popup_pos, ImGuiCond_Always);                  // Note: misleading: the value will serve as reference for FindBestWindowPosForPopup(), not actual pos.\n        PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding\n        menu_is_open = BeginPopupEx(id, window_flags);                  // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display)\n        PopStyleVar();\n        if (menu_is_open)\n        {\n            // Restore LastItemData so IsItemXXXX functions can work after BeginMenu()/EndMenu()\n            // (This fixes using IsItemClicked() and IsItemHovered(), but IsItemHovered() also relies on its support for ImGuiItemFlags_NoWindowHoverableCheck)\n            g.LastItemData = last_item_in_parent;\n            if (g.HoveredWindow == window)\n                g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow;\n        }\n    }\n    else\n    {\n        g.NextWindowData.ClearFlags(); // We behave like Begin() and need to consume those values\n    }\n\n    return menu_is_open;\n}\n\nbool ImGui::BeginMenu(const char* label, bool enabled)\n{\n    return BeginMenuEx(label, NULL, enabled);\n}\n\nvoid ImGui::EndMenu()\n{\n    // Nav: When a left move request our menu failed, close ourselves.\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup);  // Mismatched BeginMenu()/EndMenu() calls\n    ImGuiWindow* parent_window = window->ParentWindow;  // Should always be != NULL is we passed assert.\n    if (window->BeginCount == window->BeginCountPreviousFrame)\n        if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet())\n            if (g.NavWindow && (g.NavWindow->RootWindowForNav == window) && parent_window->DC.LayoutType == ImGuiLayoutType_Vertical)\n            {\n                ClosePopupToLevel(g.BeginPopupStack.Size - 1, true);\n                NavMoveRequestCancel();\n            }\n\n    EndPopup();\n}\n\nbool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut, bool selected, bool enabled)\n{\n    ImGuiWindow* window = GetCurrentWindow();\n    if (window->SkipItems)\n        return false;\n\n    ImGuiContext& g = *GImGui;\n    ImGuiStyle& style = g.Style;\n    ImVec2 pos = window->DC.CursorPos;\n    ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    // See BeginMenuEx() for comments about this.\n    const bool menuset_is_open = IsRootOfOpenMenuSet();\n    if (menuset_is_open)\n        PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true);\n\n    // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73),\n    // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only.\n    bool pressed;\n    PushID(label);\n    if (!enabled)\n        BeginDisabled();\n\n    // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another.\n    const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover;\n    const ImGuiMenuColumns* offsets = &window->DC.MenuColumns;\n    if (window->DC.LayoutType == ImGuiLayoutType_Horizontal)\n    {\n        // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful\n        // Note that in this situation: we don't render the shortcut, we render a highlight instead of the selected tick mark.\n        float w = label_size.x;\n        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * 0.5f);\n        ImVec2 text_pos(window->DC.CursorPos.x + offsets->OffsetLabel, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);\n        PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y));\n        pressed = Selectable(\"\", selected, selectable_flags, ImVec2(w, 0.0f));\n        PopStyleVar();\n        if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)\n            RenderText(text_pos, label);\n        window->DC.CursorPos.x += IM_TRUNC(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar().\n    }\n    else\n    {\n        // Menu item inside a vertical menu\n        // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.\n        //  Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.\n        float icon_w = (icon && icon[0]) ? CalcTextSize(icon, NULL).x : 0.0f;\n        float shortcut_w = (shortcut && shortcut[0]) ? CalcTextSize(shortcut, NULL).x : 0.0f;\n        float checkmark_w = IM_TRUNC(g.FontSize * 1.20f);\n        float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame\n        float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);\n        pressed = Selectable(\"\", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, label_size.y));\n        if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)\n        {\n            RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label);\n            if (icon_w > 0.0f)\n                RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon);\n            if (shortcut_w > 0.0f)\n            {\n                PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);\n                RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false);\n                PopStyleColor();\n            }\n            if (selected)\n                RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f);\n        }\n    }\n    IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0));\n    if (!enabled)\n        EndDisabled();\n    PopID();\n    if (menuset_is_open)\n        PopItemFlag();\n\n    return pressed;\n}\n\nbool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled)\n{\n    return MenuItemEx(label, NULL, shortcut, selected, enabled);\n}\n\nbool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled)\n{\n    if (MenuItemEx(label, NULL, shortcut, p_selected ? *p_selected : false, enabled))\n    {\n        if (p_selected)\n            *p_selected = !*p_selected;\n        return true;\n    }\n    return false;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: BeginTabBar, EndTabBar, etc.\n//-------------------------------------------------------------------------\n// - BeginTabBar()\n// - BeginTabBarEx() [Internal]\n// - EndTabBar()\n// - TabBarLayout() [Internal]\n// - TabBarCalcTabID() [Internal]\n// - TabBarCalcMaxTabWidth() [Internal]\n// - TabBarFindTabById() [Internal]\n// - TabBarFindTabByOrder() [Internal]\n// - TabBarGetCurrentTab() [Internal]\n// - TabBarGetTabName() [Internal]\n// - TabBarRemoveTab() [Internal]\n// - TabBarCloseTab() [Internal]\n// - TabBarScrollClamp() [Internal]\n// - TabBarScrollToTab() [Internal]\n// - TabBarQueueFocus() [Internal]\n// - TabBarQueueReorder() [Internal]\n// - TabBarProcessReorderFromMousePos() [Internal]\n// - TabBarProcessReorder() [Internal]\n// - TabBarScrollingButtons() [Internal]\n// - TabBarTabListPopupButton() [Internal]\n//-------------------------------------------------------------------------\n\nstruct ImGuiTabBarSection\n{\n    int                 TabCount;               // Number of tabs in this section.\n    float               Width;                  // Sum of width of tabs in this section (after shrinking down)\n    float               Spacing;                // Horizontal spacing at the end of the section.\n\n    ImGuiTabBarSection() { memset(this, 0, sizeof(*this)); }\n};\n\nnamespace ImGui\n{\n    static void             TabBarLayout(ImGuiTabBar* tab_bar);\n    static ImU32            TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window);\n    static float            TabBarCalcMaxTabWidth();\n    static float            TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling);\n    static void             TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections);\n    static ImGuiTabItem*    TabBarScrollingButtons(ImGuiTabBar* tab_bar);\n    static ImGuiTabItem*    TabBarTabListPopupButton(ImGuiTabBar* tab_bar);\n}\n\nImGuiTabBar::ImGuiTabBar()\n{\n    memset(this, 0, sizeof(*this));\n    CurrFrameVisible = PrevFrameVisible = -1;\n    LastTabItemIdx = -1;\n}\n\nstatic inline int TabItemGetSectionIdx(const ImGuiTabItem* tab)\n{\n    return (tab->Flags & ImGuiTabItemFlags_Leading) ? 0 : (tab->Flags & ImGuiTabItemFlags_Trailing) ? 2 : 1;\n}\n\nstatic int IMGUI_CDECL TabItemComparerBySection(const void* lhs, const void* rhs)\n{\n    const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;\n    const ImGuiTabItem* b = (const ImGuiTabItem*)rhs;\n    const int a_section = TabItemGetSectionIdx(a);\n    const int b_section = TabItemGetSectionIdx(b);\n    if (a_section != b_section)\n        return a_section - b_section;\n    return (int)(a->IndexDuringLayout - b->IndexDuringLayout);\n}\n\nstatic int IMGUI_CDECL TabItemComparerByBeginOrder(const void* lhs, const void* rhs)\n{\n    const ImGuiTabItem* a = (const ImGuiTabItem*)lhs;\n    const ImGuiTabItem* b = (const ImGuiTabItem*)rhs;\n    return (int)(a->BeginOrder - b->BeginOrder);\n}\n\nstatic ImGuiTabBar* GetTabBarFromTabBarRef(const ImGuiPtrOrIndex& ref)\n{\n    ImGuiContext& g = *GImGui;\n    return ref.Ptr ? (ImGuiTabBar*)ref.Ptr : g.TabBars.GetByIndex(ref.Index);\n}\n\nstatic ImGuiPtrOrIndex GetTabBarRefFromTabBar(ImGuiTabBar* tab_bar)\n{\n    ImGuiContext& g = *GImGui;\n    if (g.TabBars.Contains(tab_bar))\n        return ImGuiPtrOrIndex(g.TabBars.GetIndex(tab_bar));\n    return ImGuiPtrOrIndex(tab_bar);\n}\n\nbool    ImGui::BeginTabBar(const char* str_id, ImGuiTabBarFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    ImGuiID id = window->GetID(str_id);\n    ImGuiTabBar* tab_bar = g.TabBars.GetOrAddByKey(id);\n    ImRect tab_bar_bb = ImRect(window->DC.CursorPos.x, window->DC.CursorPos.y, window->WorkRect.Max.x, window->DC.CursorPos.y + g.FontSize + g.Style.FramePadding.y * 2);\n    tab_bar->ID = id;\n    tab_bar->SeparatorMinX = tab_bar->BarRect.Min.x - IM_TRUNC(window->WindowPadding.x * 0.5f);\n    tab_bar->SeparatorMaxX = tab_bar->BarRect.Max.x + IM_TRUNC(window->WindowPadding.x * 0.5f);\n    return BeginTabBarEx(tab_bar, tab_bar_bb, flags | ImGuiTabBarFlags_IsFocused);\n}\n\nbool    ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImGuiTabBarFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    IM_ASSERT(tab_bar->ID != 0);\n    if ((flags & ImGuiTabBarFlags_DockNode) == 0)\n        PushOverrideID(tab_bar->ID);\n\n    // Add to stack\n    g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar));\n    g.CurrentTabBar = tab_bar;\n\n    // Append with multiple BeginTabBar()/EndTabBar() pairs.\n    tab_bar->BackupCursorPos = window->DC.CursorPos;\n    if (tab_bar->CurrFrameVisible == g.FrameCount)\n    {\n        window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY);\n        tab_bar->BeginCount++;\n        return true;\n    }\n\n    // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable\n    if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable)))\n        ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder);\n    tab_bar->TabsAddedNew = false;\n\n    // Flags\n    if ((flags & ImGuiTabBarFlags_FittingPolicyMask_) == 0)\n        flags |= ImGuiTabBarFlags_FittingPolicyDefault_;\n\n    tab_bar->Flags = flags;\n    tab_bar->BarRect = tab_bar_bb;\n    tab_bar->WantLayout = true; // Layout will be done on the first call to ItemTab()\n    tab_bar->PrevFrameVisible = tab_bar->CurrFrameVisible;\n    tab_bar->CurrFrameVisible = g.FrameCount;\n    tab_bar->PrevTabsContentsHeight = tab_bar->CurrTabsContentsHeight;\n    tab_bar->CurrTabsContentsHeight = 0.0f;\n    tab_bar->ItemSpacingY = g.Style.ItemSpacing.y;\n    tab_bar->FramePadding = g.Style.FramePadding;\n    tab_bar->TabsActiveCount = 0;\n    tab_bar->LastTabItemIdx = -1;\n    tab_bar->BeginCount = 1;\n\n    // Set cursor pos in a way which only be used in the off-chance the user erroneously submits item before BeginTabItem(): items will overlap\n    window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.y + tab_bar->ItemSpacingY);\n\n    // Draw separator\n    // (it would be misleading to draw this in EndTabBar() suggesting that it may be drawn over tabs, as tab bar are appendable)\n    const ImU32 col = GetColorU32((flags & ImGuiTabBarFlags_IsFocused) ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive);\n    if (g.Style.TabBarBorderSize > 0.0f)\n    {\n        const float y = tab_bar->BarRect.Max.y;\n        window->DrawList->AddRectFilled(ImVec2(tab_bar->SeparatorMinX, y - g.Style.TabBarBorderSize), ImVec2(tab_bar->SeparatorMaxX, y), col);\n    }\n    return true;\n}\n\nvoid    ImGui::EndTabBar()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    ImGuiTabBar* tab_bar = g.CurrentTabBar;\n    if (tab_bar == NULL)\n    {\n        IM_ASSERT_USER_ERROR(tab_bar != NULL, \"Mismatched BeginTabBar()/EndTabBar()!\");\n        return;\n    }\n\n    // Fallback in case no TabItem have been submitted\n    if (tab_bar->WantLayout)\n        TabBarLayout(tab_bar);\n\n    // Restore the last visible height if no tab is visible, this reduce vertical flicker/movement when a tabs gets removed without calling SetTabItemClosed().\n    const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);\n    if (tab_bar->VisibleTabWasSubmitted || tab_bar->VisibleTabId == 0 || tab_bar_appearing)\n    {\n        tab_bar->CurrTabsContentsHeight = ImMax(window->DC.CursorPos.y - tab_bar->BarRect.Max.y, tab_bar->CurrTabsContentsHeight);\n        window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->CurrTabsContentsHeight;\n    }\n    else\n    {\n        window->DC.CursorPos.y = tab_bar->BarRect.Max.y + tab_bar->PrevTabsContentsHeight;\n    }\n    if (tab_bar->BeginCount > 1)\n        window->DC.CursorPos = tab_bar->BackupCursorPos;\n\n    tab_bar->LastTabItemIdx = -1;\n    if ((tab_bar->Flags & ImGuiTabBarFlags_DockNode) == 0)\n        PopID();\n\n    g.CurrentTabBarStack.pop_back();\n    g.CurrentTabBar = g.CurrentTabBarStack.empty() ? NULL : GetTabBarFromTabBarRef(g.CurrentTabBarStack.back());\n}\n\n// Scrolling happens only in the central section (leading/trailing sections are not scrolling)\nstatic float TabBarCalcScrollableWidth(ImGuiTabBar* tab_bar, ImGuiTabBarSection* sections)\n{\n    return tab_bar->BarRect.GetWidth() - sections[0].Width - sections[2].Width - sections[1].Spacing;\n}\n\n// This is called only once a frame before by the first call to ItemTab()\n// The reason we're not calling it in BeginTabBar() is to leave a chance to the user to call the SetTabItemClosed() functions.\nstatic void ImGui::TabBarLayout(ImGuiTabBar* tab_bar)\n{\n    ImGuiContext& g = *GImGui;\n    tab_bar->WantLayout = false;\n\n    // Garbage collect by compacting list\n    // Detect if we need to sort out tab list (e.g. in rare case where a tab changed section)\n    int tab_dst_n = 0;\n    bool need_sort_by_section = false;\n    ImGuiTabBarSection sections[3]; // Layout sections: Leading, Central, Trailing\n    for (int tab_src_n = 0; tab_src_n < tab_bar->Tabs.Size; tab_src_n++)\n    {\n        ImGuiTabItem* tab = &tab_bar->Tabs[tab_src_n];\n        if (tab->LastFrameVisible < tab_bar->PrevFrameVisible || tab->WantClose)\n        {\n            // Remove tab\n            if (tab_bar->VisibleTabId == tab->ID) { tab_bar->VisibleTabId = 0; }\n            if (tab_bar->SelectedTabId == tab->ID) { tab_bar->SelectedTabId = 0; }\n            if (tab_bar->NextSelectedTabId == tab->ID) { tab_bar->NextSelectedTabId = 0; }\n            continue;\n        }\n        if (tab_dst_n != tab_src_n)\n            tab_bar->Tabs[tab_dst_n] = tab_bar->Tabs[tab_src_n];\n\n        tab = &tab_bar->Tabs[tab_dst_n];\n        tab->IndexDuringLayout = (ImS16)tab_dst_n;\n\n        // We will need sorting if tabs have changed section (e.g. moved from one of Leading/Central/Trailing to another)\n        int curr_tab_section_n = TabItemGetSectionIdx(tab);\n        if (tab_dst_n > 0)\n        {\n            ImGuiTabItem* prev_tab = &tab_bar->Tabs[tab_dst_n - 1];\n            int prev_tab_section_n = TabItemGetSectionIdx(prev_tab);\n            if (curr_tab_section_n == 0 && prev_tab_section_n != 0)\n                need_sort_by_section = true;\n            if (prev_tab_section_n == 2 && curr_tab_section_n != 2)\n                need_sort_by_section = true;\n        }\n\n        sections[curr_tab_section_n].TabCount++;\n        tab_dst_n++;\n    }\n    if (tab_bar->Tabs.Size != tab_dst_n)\n        tab_bar->Tabs.resize(tab_dst_n);\n\n    if (need_sort_by_section)\n        ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerBySection);\n\n    // Calculate spacing between sections\n    sections[0].Spacing = sections[0].TabCount > 0 && (sections[1].TabCount + sections[2].TabCount) > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;\n    sections[1].Spacing = sections[1].TabCount > 0 && sections[2].TabCount > 0 ? g.Style.ItemInnerSpacing.x : 0.0f;\n\n    // Setup next selected tab\n    ImGuiID scroll_to_tab_id = 0;\n    if (tab_bar->NextSelectedTabId)\n    {\n        tab_bar->SelectedTabId = tab_bar->NextSelectedTabId;\n        tab_bar->NextSelectedTabId = 0;\n        scroll_to_tab_id = tab_bar->SelectedTabId;\n    }\n\n    // Process order change request (we could probably process it when requested but it's just saner to do it in a single spot).\n    if (tab_bar->ReorderRequestTabId != 0)\n    {\n        if (TabBarProcessReorder(tab_bar))\n            if (tab_bar->ReorderRequestTabId == tab_bar->SelectedTabId)\n                scroll_to_tab_id = tab_bar->ReorderRequestTabId;\n        tab_bar->ReorderRequestTabId = 0;\n    }\n\n    // Tab List Popup (will alter tab_bar->BarRect and therefore the available width!)\n    const bool tab_list_popup_button = (tab_bar->Flags & ImGuiTabBarFlags_TabListPopupButton) != 0;\n    if (tab_list_popup_button)\n        if (ImGuiTabItem* tab_to_select = TabBarTabListPopupButton(tab_bar)) // NB: Will alter BarRect.Min.x!\n            scroll_to_tab_id = tab_bar->SelectedTabId = tab_to_select->ID;\n\n    // Leading/Trailing tabs will be shrink only if central one aren't visible anymore, so layout the shrink data as: leading, trailing, central\n    // (whereas our tabs are stored as: leading, central, trailing)\n    int shrink_buffer_indexes[3] = { 0, sections[0].TabCount + sections[2].TabCount, sections[0].TabCount };\n    g.ShrinkWidthBuffer.resize(tab_bar->Tabs.Size);\n\n    // Compute ideal tabs widths + store them into shrink buffer\n    ImGuiTabItem* most_recently_selected_tab = NULL;\n    int curr_section_n = -1;\n    bool found_selected_tab_id = false;\n    for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)\n    {\n        ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];\n        IM_ASSERT(tab->LastFrameVisible >= tab_bar->PrevFrameVisible);\n\n        if ((most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) && !(tab->Flags & ImGuiTabItemFlags_Button))\n            most_recently_selected_tab = tab;\n        if (tab->ID == tab_bar->SelectedTabId)\n            found_selected_tab_id = true;\n        if (scroll_to_tab_id == 0 && g.NavJustMovedToId == tab->ID)\n            scroll_to_tab_id = tab->ID;\n\n        // Refresh tab width immediately, otherwise changes of style e.g. style.FramePadding.x would noticeably lag in the tab bar.\n        // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet,\n        // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window.\n        const char* tab_name = TabBarGetTabName(tab_bar, tab);\n        const bool has_close_button_or_unsaved_marker = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0 || (tab->Flags & ImGuiTabItemFlags_UnsavedDocument);\n        tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x;\n\n        int section_n = TabItemGetSectionIdx(tab);\n        ImGuiTabBarSection* section = &sections[section_n];\n        section->Width += tab->ContentWidth + (section_n == curr_section_n ? g.Style.ItemInnerSpacing.x : 0.0f);\n        curr_section_n = section_n;\n\n        // Store data so we can build an array sorted by width if we need to shrink tabs down\n        IM_MSVC_WARNING_SUPPRESS(6385);\n        ImGuiShrinkWidthItem* shrink_width_item = &g.ShrinkWidthBuffer[shrink_buffer_indexes[section_n]++];\n        shrink_width_item->Index = tab_n;\n        shrink_width_item->Width = shrink_width_item->InitialWidth = tab->ContentWidth;\n        tab->Width = ImMax(tab->ContentWidth, 1.0f);\n    }\n\n    // Compute total ideal width (used for e.g. auto-resizing a window)\n    tab_bar->WidthAllTabsIdeal = 0.0f;\n    for (int section_n = 0; section_n < 3; section_n++)\n        tab_bar->WidthAllTabsIdeal += sections[section_n].Width + sections[section_n].Spacing;\n\n    // Horizontal scrolling buttons\n    // (note that TabBarScrollButtons() will alter BarRect.Max.x)\n    if ((tab_bar->WidthAllTabsIdeal > tab_bar->BarRect.GetWidth() && tab_bar->Tabs.Size > 1) && !(tab_bar->Flags & ImGuiTabBarFlags_NoTabListScrollingButtons) && (tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll))\n        if (ImGuiTabItem* scroll_and_select_tab = TabBarScrollingButtons(tab_bar))\n        {\n            scroll_to_tab_id = scroll_and_select_tab->ID;\n            if ((scroll_and_select_tab->Flags & ImGuiTabItemFlags_Button) == 0)\n                tab_bar->SelectedTabId = scroll_to_tab_id;\n        }\n\n    // Shrink widths if full tabs don't fit in their allocated space\n    float section_0_w = sections[0].Width + sections[0].Spacing;\n    float section_1_w = sections[1].Width + sections[1].Spacing;\n    float section_2_w = sections[2].Width + sections[2].Spacing;\n    bool central_section_is_visible = (section_0_w + section_2_w) < tab_bar->BarRect.GetWidth();\n    float width_excess;\n    if (central_section_is_visible)\n        width_excess = ImMax(section_1_w - (tab_bar->BarRect.GetWidth() - section_0_w - section_2_w), 0.0f); // Excess used to shrink central section\n    else\n        width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth(); // Excess used to shrink leading/trailing section\n\n    // With ImGuiTabBarFlags_FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore\n    if (width_excess >= 1.0f && ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || !central_section_is_visible))\n    {\n        int shrink_data_count = (central_section_is_visible ? sections[1].TabCount : sections[0].TabCount + sections[2].TabCount);\n        int shrink_data_offset = (central_section_is_visible ? sections[0].TabCount + sections[2].TabCount : 0);\n        ShrinkWidths(g.ShrinkWidthBuffer.Data + shrink_data_offset, shrink_data_count, width_excess);\n\n        // Apply shrunk values into tabs and sections\n        for (int tab_n = shrink_data_offset; tab_n < shrink_data_offset + shrink_data_count; tab_n++)\n        {\n            ImGuiTabItem* tab = &tab_bar->Tabs[g.ShrinkWidthBuffer[tab_n].Index];\n            float shrinked_width = IM_TRUNC(g.ShrinkWidthBuffer[tab_n].Width);\n            if (shrinked_width < 0.0f)\n                continue;\n\n            shrinked_width = ImMax(1.0f, shrinked_width);\n            int section_n = TabItemGetSectionIdx(tab);\n            sections[section_n].Width -= (tab->Width - shrinked_width);\n            tab->Width = shrinked_width;\n        }\n    }\n\n    // Layout all active tabs\n    int section_tab_index = 0;\n    float tab_offset = 0.0f;\n    tab_bar->WidthAllTabs = 0.0f;\n    for (int section_n = 0; section_n < 3; section_n++)\n    {\n        ImGuiTabBarSection* section = &sections[section_n];\n        if (section_n == 2)\n            tab_offset = ImMin(ImMax(0.0f, tab_bar->BarRect.GetWidth() - section->Width), tab_offset);\n\n        for (int tab_n = 0; tab_n < section->TabCount; tab_n++)\n        {\n            ImGuiTabItem* tab = &tab_bar->Tabs[section_tab_index + tab_n];\n            tab->Offset = tab_offset;\n            tab->NameOffset = -1;\n            tab_offset += tab->Width + (tab_n < section->TabCount - 1 ? g.Style.ItemInnerSpacing.x : 0.0f);\n        }\n        tab_bar->WidthAllTabs += ImMax(section->Width + section->Spacing, 0.0f);\n        tab_offset += section->Spacing;\n        section_tab_index += section->TabCount;\n    }\n\n    // Clear name buffers\n    tab_bar->TabsNames.Buf.resize(0);\n\n    // If we have lost the selected tab, select the next most recently active one\n    if (found_selected_tab_id == false)\n        tab_bar->SelectedTabId = 0;\n    if (tab_bar->SelectedTabId == 0 && tab_bar->NextSelectedTabId == 0 && most_recently_selected_tab != NULL)\n        scroll_to_tab_id = tab_bar->SelectedTabId = most_recently_selected_tab->ID;\n\n    // Lock in visible tab\n    tab_bar->VisibleTabId = tab_bar->SelectedTabId;\n    tab_bar->VisibleTabWasSubmitted = false;\n\n    // Apply request requests\n    if (scroll_to_tab_id != 0)\n        TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections);\n    else if ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyScroll) && IsMouseHoveringRect(tab_bar->BarRect.Min, tab_bar->BarRect.Max, true) && IsWindowContentHoverable(g.CurrentWindow))\n    {\n        const float wheel = g.IO.MouseWheelRequestAxisSwap ? g.IO.MouseWheel : g.IO.MouseWheelH;\n        const ImGuiKey wheel_key = g.IO.MouseWheelRequestAxisSwap ? ImGuiKey_MouseWheelY : ImGuiKey_MouseWheelX;\n        if (TestKeyOwner(wheel_key, tab_bar->ID) && wheel != 0.0f)\n        {\n            const float scroll_step = wheel * TabBarCalcScrollableWidth(tab_bar, sections) / 3.0f;\n            tab_bar->ScrollingTargetDistToVisibility = 0.0f;\n            tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget - scroll_step);\n        }\n        SetKeyOwner(wheel_key, tab_bar->ID);\n    }\n\n    // Update scrolling\n    tab_bar->ScrollingAnim = TabBarScrollClamp(tab_bar, tab_bar->ScrollingAnim);\n    tab_bar->ScrollingTarget = TabBarScrollClamp(tab_bar, tab_bar->ScrollingTarget);\n    if (tab_bar->ScrollingAnim != tab_bar->ScrollingTarget)\n    {\n        // Scrolling speed adjust itself so we can always reach our target in 1/3 seconds.\n        // Teleport if we are aiming far off the visible line\n        tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, 70.0f * g.FontSize);\n        tab_bar->ScrollingSpeed = ImMax(tab_bar->ScrollingSpeed, ImFabs(tab_bar->ScrollingTarget - tab_bar->ScrollingAnim) / 0.3f);\n        const bool teleport = (tab_bar->PrevFrameVisible + 1 < g.FrameCount) || (tab_bar->ScrollingTargetDistToVisibility > 10.0f * g.FontSize);\n        tab_bar->ScrollingAnim = teleport ? tab_bar->ScrollingTarget : ImLinearSweep(tab_bar->ScrollingAnim, tab_bar->ScrollingTarget, g.IO.DeltaTime * tab_bar->ScrollingSpeed);\n    }\n    else\n    {\n        tab_bar->ScrollingSpeed = 0.0f;\n    }\n    tab_bar->ScrollingRectMinX = tab_bar->BarRect.Min.x + sections[0].Width + sections[0].Spacing;\n    tab_bar->ScrollingRectMaxX = tab_bar->BarRect.Max.x - sections[2].Width - sections[1].Spacing;\n\n    // Actual layout in host window (we don't do it in BeginTabBar() so as not to waste an extra frame)\n    ImGuiWindow* window = g.CurrentWindow;\n    window->DC.CursorPos = tab_bar->BarRect.Min;\n    ItemSize(ImVec2(tab_bar->WidthAllTabs, tab_bar->BarRect.GetHeight()), tab_bar->FramePadding.y);\n    window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, tab_bar->BarRect.Min.x + tab_bar->WidthAllTabsIdeal);\n}\n\n// Dockable windows uses Name/ID in the global namespace. Non-dockable items use the ID stack.\nstatic ImU32   ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window)\n{\n    IM_ASSERT(docked_window == NULL); // master branch only\n    IM_UNUSED(docked_window);\n    if (tab_bar->Flags & ImGuiTabBarFlags_DockNode)\n    {\n        ImGuiID id = ImHashStr(label);\n        KeepAliveID(id);\n        return id;\n    }\n    else\n    {\n        ImGuiWindow* window = GImGui->CurrentWindow;\n        return window->GetID(label);\n    }\n}\n\nstatic float ImGui::TabBarCalcMaxTabWidth()\n{\n    ImGuiContext& g = *GImGui;\n    return g.FontSize * 20.0f;\n}\n\nImGuiTabItem* ImGui::TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id)\n{\n    if (tab_id != 0)\n        for (int n = 0; n < tab_bar->Tabs.Size; n++)\n            if (tab_bar->Tabs[n].ID == tab_id)\n                return &tab_bar->Tabs[n];\n    return NULL;\n}\n\n// Order = visible order, not submission order! (which is tab->BeginOrder)\nImGuiTabItem* ImGui::TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order)\n{\n    if (order < 0 || order >= tab_bar->Tabs.Size)\n        return NULL;\n    return &tab_bar->Tabs[order];\n}\n\nImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar)\n{\n    if (tab_bar->LastTabItemIdx <= 0 || tab_bar->LastTabItemIdx >= tab_bar->Tabs.Size)\n        return NULL;\n    return &tab_bar->Tabs[tab_bar->LastTabItemIdx];\n}\n\nconst char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)\n{\n    if (tab->NameOffset == -1)\n        return \"N/A\";\n    IM_ASSERT(tab->NameOffset < tab_bar->TabsNames.Buf.Size);\n    return tab_bar->TabsNames.Buf.Data + tab->NameOffset;\n}\n\n// The *TabId fields are already set by the docking system _before_ the actual TabItem was created, so we clear them regardless.\nvoid ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id)\n{\n    if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))\n        tab_bar->Tabs.erase(tab);\n    if (tab_bar->VisibleTabId == tab_id)      { tab_bar->VisibleTabId = 0; }\n    if (tab_bar->SelectedTabId == tab_id)     { tab_bar->SelectedTabId = 0; }\n    if (tab_bar->NextSelectedTabId == tab_id) { tab_bar->NextSelectedTabId = 0; }\n}\n\n// Called on manual closure attempt\nvoid ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)\n{\n    if (tab->Flags & ImGuiTabItemFlags_Button)\n        return; // A button appended with TabItemButton().\n\n    if (!(tab->Flags & ImGuiTabItemFlags_UnsavedDocument))\n    {\n        // This will remove a frame of lag for selecting another tab on closure.\n        // However we don't run it in the case where the 'Unsaved' flag is set, so user gets a chance to fully undo the closure\n        tab->WantClose = true;\n        if (tab_bar->VisibleTabId == tab->ID)\n        {\n            tab->LastFrameVisible = -1;\n            tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = 0;\n        }\n    }\n    else\n    {\n        // Actually select before expecting closure attempt (on an UnsavedDocument tab user is expect to e.g. show a popup)\n        if (tab_bar->VisibleTabId != tab->ID)\n            TabBarQueueFocus(tab_bar, tab);\n    }\n}\n\nstatic float ImGui::TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling)\n{\n    scrolling = ImMin(scrolling, tab_bar->WidthAllTabs - tab_bar->BarRect.GetWidth());\n    return ImMax(scrolling, 0.0f);\n}\n\n// Note: we may scroll to tab that are not selected! e.g. using keyboard arrow keys\nstatic void ImGui::TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections)\n{\n    ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id);\n    if (tab == NULL)\n        return;\n    if (tab->Flags & ImGuiTabItemFlags_SectionMask_)\n        return;\n\n    ImGuiContext& g = *GImGui;\n    float margin = g.FontSize * 1.0f; // When to scroll to make Tab N+1 visible always make a bit of N visible to suggest more scrolling area (since we don't have a scrollbar)\n    int order = TabBarGetTabOrder(tab_bar, tab);\n\n    // Scrolling happens only in the central section (leading/trailing sections are not scrolling)\n    float scrollable_width = TabBarCalcScrollableWidth(tab_bar, sections);\n\n    // We make all tabs positions all relative Sections[0].Width to make code simpler\n    float tab_x1 = tab->Offset - sections[0].Width + (order > sections[0].TabCount - 1 ? -margin : 0.0f);\n    float tab_x2 = tab->Offset - sections[0].Width + tab->Width + (order + 1 < tab_bar->Tabs.Size - sections[2].TabCount ? margin : 1.0f);\n    tab_bar->ScrollingTargetDistToVisibility = 0.0f;\n    if (tab_bar->ScrollingTarget > tab_x1 || (tab_x2 - tab_x1 >= scrollable_width))\n    {\n        // Scroll to the left\n        tab_bar->ScrollingTargetDistToVisibility = ImMax(tab_bar->ScrollingAnim - tab_x2, 0.0f);\n        tab_bar->ScrollingTarget = tab_x1;\n    }\n    else if (tab_bar->ScrollingTarget < tab_x2 - scrollable_width)\n    {\n        // Scroll to the right\n        tab_bar->ScrollingTargetDistToVisibility = ImMax((tab_x1 - scrollable_width) - tab_bar->ScrollingAnim, 0.0f);\n        tab_bar->ScrollingTarget = tab_x2 - scrollable_width;\n    }\n}\n\nvoid ImGui::TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab)\n{\n    tab_bar->NextSelectedTabId = tab->ID;\n}\n\nvoid ImGui::TabBarQueueReorder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, int offset)\n{\n    IM_ASSERT(offset != 0);\n    IM_ASSERT(tab_bar->ReorderRequestTabId == 0);\n    tab_bar->ReorderRequestTabId = tab->ID;\n    tab_bar->ReorderRequestOffset = (ImS16)offset;\n}\n\nvoid ImGui::TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* src_tab, ImVec2 mouse_pos)\n{\n    ImGuiContext& g = *GImGui;\n    IM_ASSERT(tab_bar->ReorderRequestTabId == 0);\n    if ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) == 0)\n        return;\n\n    const bool is_central_section = (src_tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;\n    const float bar_offset = tab_bar->BarRect.Min.x - (is_central_section ? tab_bar->ScrollingTarget : 0);\n\n    // Count number of contiguous tabs we are crossing over\n    const int dir = (bar_offset + src_tab->Offset) > mouse_pos.x ? -1 : +1;\n    const int src_idx = tab_bar->Tabs.index_from_ptr(src_tab);\n    int dst_idx = src_idx;\n    for (int i = src_idx; i >= 0 && i < tab_bar->Tabs.Size; i += dir)\n    {\n        // Reordered tabs must share the same section\n        const ImGuiTabItem* dst_tab = &tab_bar->Tabs[i];\n        if (dst_tab->Flags & ImGuiTabItemFlags_NoReorder)\n            break;\n        if ((dst_tab->Flags & ImGuiTabItemFlags_SectionMask_) != (src_tab->Flags & ImGuiTabItemFlags_SectionMask_))\n            break;\n        dst_idx = i;\n\n        // Include spacing after tab, so when mouse cursor is between tabs we would not continue checking further tabs that are not hovered.\n        const float x1 = bar_offset + dst_tab->Offset - g.Style.ItemInnerSpacing.x;\n        const float x2 = bar_offset + dst_tab->Offset + dst_tab->Width + g.Style.ItemInnerSpacing.x;\n        //GetForegroundDrawList()->AddRect(ImVec2(x1, tab_bar->BarRect.Min.y), ImVec2(x2, tab_bar->BarRect.Max.y), IM_COL32(255, 0, 0, 255));\n        if ((dir < 0 && mouse_pos.x > x1) || (dir > 0 && mouse_pos.x < x2))\n            break;\n    }\n\n    if (dst_idx != src_idx)\n        TabBarQueueReorder(tab_bar, src_tab, dst_idx - src_idx);\n}\n\nbool ImGui::TabBarProcessReorder(ImGuiTabBar* tab_bar)\n{\n    ImGuiTabItem* tab1 = TabBarFindTabByID(tab_bar, tab_bar->ReorderRequestTabId);\n    if (tab1 == NULL || (tab1->Flags & ImGuiTabItemFlags_NoReorder))\n        return false;\n\n    //IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_Reorderable); // <- this may happen when using debug tools\n    int tab2_order = TabBarGetTabOrder(tab_bar, tab1) + tab_bar->ReorderRequestOffset;\n    if (tab2_order < 0 || tab2_order >= tab_bar->Tabs.Size)\n        return false;\n\n    // Reordered tabs must share the same section\n    // (Note: TabBarQueueReorderFromMousePos() also has a similar test but since we allow direct calls to TabBarQueueReorder() we do it here too)\n    ImGuiTabItem* tab2 = &tab_bar->Tabs[tab2_order];\n    if (tab2->Flags & ImGuiTabItemFlags_NoReorder)\n        return false;\n    if ((tab1->Flags & ImGuiTabItemFlags_SectionMask_) != (tab2->Flags & ImGuiTabItemFlags_SectionMask_))\n        return false;\n\n    ImGuiTabItem item_tmp = *tab1;\n    ImGuiTabItem* src_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 + 1 : tab2;\n    ImGuiTabItem* dst_tab = (tab_bar->ReorderRequestOffset > 0) ? tab1 : tab2 + 1;\n    const int move_count = (tab_bar->ReorderRequestOffset > 0) ? tab_bar->ReorderRequestOffset : -tab_bar->ReorderRequestOffset;\n    memmove(dst_tab, src_tab, move_count * sizeof(ImGuiTabItem));\n    *tab2 = item_tmp;\n\n    if (tab_bar->Flags & ImGuiTabBarFlags_SaveSettings)\n        MarkIniSettingsDirty();\n    return true;\n}\n\nstatic ImGuiTabItem* ImGui::TabBarScrollingButtons(ImGuiTabBar* tab_bar)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    const ImVec2 arrow_button_size(g.FontSize - 2.0f, g.FontSize + g.Style.FramePadding.y * 2.0f);\n    const float scrolling_buttons_width = arrow_button_size.x * 2.0f;\n\n    const ImVec2 backup_cursor_pos = window->DC.CursorPos;\n    //window->DrawList->AddRect(ImVec2(tab_bar->BarRect.Max.x - scrolling_buttons_width, tab_bar->BarRect.Min.y), ImVec2(tab_bar->BarRect.Max.x, tab_bar->BarRect.Max.y), IM_COL32(255,0,0,255));\n\n    int select_dir = 0;\n    ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];\n    arrow_col.w *= 0.5f;\n\n    PushStyleColor(ImGuiCol_Text, arrow_col);\n    PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));\n    const float backup_repeat_delay = g.IO.KeyRepeatDelay;\n    const float backup_repeat_rate = g.IO.KeyRepeatRate;\n    g.IO.KeyRepeatDelay = 0.250f;\n    g.IO.KeyRepeatRate = 0.200f;\n    float x = ImMax(tab_bar->BarRect.Min.x, tab_bar->BarRect.Max.x - scrolling_buttons_width);\n    window->DC.CursorPos = ImVec2(x, tab_bar->BarRect.Min.y);\n    if (ArrowButtonEx(\"##<\", ImGuiDir_Left, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))\n        select_dir = -1;\n    window->DC.CursorPos = ImVec2(x + arrow_button_size.x, tab_bar->BarRect.Min.y);\n    if (ArrowButtonEx(\"##>\", ImGuiDir_Right, arrow_button_size, ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_Repeat))\n        select_dir = +1;\n    PopStyleColor(2);\n    g.IO.KeyRepeatRate = backup_repeat_rate;\n    g.IO.KeyRepeatDelay = backup_repeat_delay;\n\n    ImGuiTabItem* tab_to_scroll_to = NULL;\n    if (select_dir != 0)\n        if (ImGuiTabItem* tab_item = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId))\n        {\n            int selected_order = TabBarGetTabOrder(tab_bar, tab_item);\n            int target_order = selected_order + select_dir;\n\n            // Skip tab item buttons until another tab item is found or end is reached\n            while (tab_to_scroll_to == NULL)\n            {\n                // If we are at the end of the list, still scroll to make our tab visible\n                tab_to_scroll_to = &tab_bar->Tabs[(target_order >= 0 && target_order < tab_bar->Tabs.Size) ? target_order : selected_order];\n\n                // Cross through buttons\n                // (even if first/last item is a button, return it so we can update the scroll)\n                if (tab_to_scroll_to->Flags & ImGuiTabItemFlags_Button)\n                {\n                    target_order += select_dir;\n                    selected_order += select_dir;\n                    tab_to_scroll_to = (target_order < 0 || target_order >= tab_bar->Tabs.Size) ? tab_to_scroll_to : NULL;\n                }\n            }\n        }\n    window->DC.CursorPos = backup_cursor_pos;\n    tab_bar->BarRect.Max.x -= scrolling_buttons_width + 1.0f;\n\n    return tab_to_scroll_to;\n}\n\nstatic ImGuiTabItem* ImGui::TabBarTabListPopupButton(ImGuiTabBar* tab_bar)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n\n    // We use g.Style.FramePadding.y to match the square ArrowButton size\n    const float tab_list_popup_button_width = g.FontSize + g.Style.FramePadding.y;\n    const ImVec2 backup_cursor_pos = window->DC.CursorPos;\n    window->DC.CursorPos = ImVec2(tab_bar->BarRect.Min.x - g.Style.FramePadding.y, tab_bar->BarRect.Min.y);\n    tab_bar->BarRect.Min.x += tab_list_popup_button_width;\n\n    ImVec4 arrow_col = g.Style.Colors[ImGuiCol_Text];\n    arrow_col.w *= 0.5f;\n    PushStyleColor(ImGuiCol_Text, arrow_col);\n    PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));\n    bool open = BeginCombo(\"##v\", NULL, ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLargest);\n    PopStyleColor(2);\n\n    ImGuiTabItem* tab_to_select = NULL;\n    if (open)\n    {\n        for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++)\n        {\n            ImGuiTabItem* tab = &tab_bar->Tabs[tab_n];\n            if (tab->Flags & ImGuiTabItemFlags_Button)\n                continue;\n\n            const char* tab_name = TabBarGetTabName(tab_bar, tab);\n            if (Selectable(tab_name, tab_bar->SelectedTabId == tab->ID))\n                tab_to_select = tab;\n        }\n        EndCombo();\n    }\n\n    window->DC.CursorPos = backup_cursor_pos;\n    return tab_to_select;\n}\n\n//-------------------------------------------------------------------------\n// [SECTION] Widgets: BeginTabItem, EndTabItem, etc.\n//-------------------------------------------------------------------------\n// - BeginTabItem()\n// - EndTabItem()\n// - TabItemButton()\n// - TabItemEx() [Internal]\n// - SetTabItemClosed()\n// - TabItemCalcSize() [Internal]\n// - TabItemBackground() [Internal]\n// - TabItemLabelAndCloseButton() [Internal]\n//-------------------------------------------------------------------------\n\nbool    ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    ImGuiTabBar* tab_bar = g.CurrentTabBar;\n    if (tab_bar == NULL)\n    {\n        IM_ASSERT_USER_ERROR(tab_bar, \"Needs to be called between BeginTabBar() and EndTabBar()!\");\n        return false;\n    }\n    IM_ASSERT(!(flags & ImGuiTabItemFlags_Button)); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead!\n\n    bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL);\n    if (ret && !(flags & ImGuiTabItemFlags_NoPushId))\n    {\n        ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];\n        PushOverrideID(tab->ID); // We already hashed 'label' so push into the ID stack directly instead of doing another hash through PushID(label)\n    }\n    return ret;\n}\n\nvoid    ImGui::EndTabItem()\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return;\n\n    ImGuiTabBar* tab_bar = g.CurrentTabBar;\n    if (tab_bar == NULL)\n    {\n        IM_ASSERT_USER_ERROR(tab_bar != NULL, \"Needs to be called between BeginTabBar() and EndTabBar()!\");\n        return;\n    }\n    IM_ASSERT(tab_bar->LastTabItemIdx >= 0);\n    ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx];\n    if (!(tab->Flags & ImGuiTabItemFlags_NoPushId))\n        PopID();\n}\n\nbool    ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags)\n{\n    ImGuiContext& g = *GImGui;\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    ImGuiTabBar* tab_bar = g.CurrentTabBar;\n    if (tab_bar == NULL)\n    {\n        IM_ASSERT_USER_ERROR(tab_bar != NULL, \"Needs to be called between BeginTabBar() and EndTabBar()!\");\n        return false;\n    }\n    return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL);\n}\n\nbool    ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window)\n{\n    // Layout whole tab bar if not already done\n    ImGuiContext& g = *GImGui;\n    if (tab_bar->WantLayout)\n    {\n        ImGuiNextItemData backup_next_item_data = g.NextItemData;\n        TabBarLayout(tab_bar);\n        g.NextItemData = backup_next_item_data;\n    }\n    ImGuiWindow* window = g.CurrentWindow;\n    if (window->SkipItems)\n        return false;\n\n    const ImGuiStyle& style = g.Style;\n    const ImGuiID id = TabBarCalcTabID(tab_bar, label, docked_window);\n\n    // If the user called us with *p_open == false, we early out and don't render.\n    // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID.\n    IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags);\n    if (p_open && !*p_open)\n    {\n        ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav);\n        return false;\n    }\n\n    IM_ASSERT(!p_open || !(flags & ImGuiTabItemFlags_Button));\n    IM_ASSERT((flags & (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)) != (ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing)); // Can't use both Leading and Trailing\n\n    // Store into ImGuiTabItemFlags_NoCloseButton, also honor ImGuiTabItemFlags_NoCloseButton passed by user (although not documented)\n    if (flags & ImGuiTabItemFlags_NoCloseButton)\n        p_open = NULL;\n    else if (p_open == NULL)\n        flags |= ImGuiTabItemFlags_NoCloseButton;\n\n    // Acquire tab data\n    ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, id);\n    bool tab_is_new = false;\n    if (tab == NULL)\n    {\n        tab_bar->Tabs.push_back(ImGuiTabItem());\n        tab = &tab_bar->Tabs.back();\n        tab->ID = id;\n        tab_bar->TabsAddedNew = tab_is_new = true;\n    }\n    tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab);\n\n    // Calculate tab contents size\n    ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument));\n    tab->RequestedWidth = -1.0f;\n    if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth)\n        size.x = tab->RequestedWidth = g.NextItemData.Width;\n    if (tab_is_new)\n        tab->Width = ImMax(1.0f, size.x);\n    tab->ContentWidth = size.x;\n    tab->BeginOrder = tab_bar->TabsActiveCount++;\n\n    const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount);\n    const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0;\n    const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount);\n    const bool tab_just_unsaved = (flags & ImGuiTabItemFlags_UnsavedDocument) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument);\n    const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0;\n    tab->LastFrameVisible = g.FrameCount;\n    tab->Flags = flags;\n\n    // Append name _WITH_ the zero-terminator\n    if (docked_window != NULL)\n    {\n        IM_ASSERT(docked_window == NULL); // master branch only\n    }\n    else\n    {\n        tab->NameOffset = (ImS32)tab_bar->TabsNames.size();\n        tab_bar->TabsNames.append(label, label + strlen(label) + 1);\n    }\n\n    // Update selected tab\n    if (!is_tab_button)\n    {\n        if (tab_appearing && (tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs) && tab_bar->NextSelectedTabId == 0)\n            if (!tab_bar_appearing || tab_bar->SelectedTabId == 0)\n                TabBarQueueFocus(tab_bar, tab); // New tabs gets activated\n        if ((flags & ImGuiTabItemFlags_SetSelected) && (tab_bar->SelectedTabId != id)) // _SetSelected can only be passed on explicit tab bar\n            TabBarQueueFocus(tab_bar, tab);\n    }\n\n    // Lock visibility\n    // (Note: tab_contents_visible != tab_selected... because CTRL+TAB operations may preview some tabs without selecting them!)\n    bool tab_contents_visible = (tab_bar->VisibleTabId == id);\n    if (tab_contents_visible)\n        tab_bar->VisibleTabWasSubmitted = true;\n\n    // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches\n    if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing)\n        if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs))\n            tab_contents_visible = true;\n\n    // Note that tab_is_new is not necessarily the same as tab_appearing! When a tab bar stops being submitted\n    // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'.\n    if (tab_appearing && (!tab_bar_appearing || tab_is_new))\n    {\n        ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav);\n        if (is_tab_button)\n            return false;\n        return tab_contents_visible;\n    }\n\n    if (tab_bar->SelectedTabId == id)\n        tab->LastFrameSelected = g.FrameCount;\n\n    // Backup current layout position\n    const ImVec2 backup_main_cursor_pos = window->DC.CursorPos;\n\n    // Layout\n    const bool is_central_section = (tab->Flags & ImGuiTabItemFlags_SectionMask_) == 0;\n    size.x = tab->Width;\n    if (is_central_section)\n        window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(IM_TRUNC(tab->Offset - tab_bar->ScrollingAnim), 0.0f);\n    else\n        window->DC.CursorPos = tab_bar->BarRect.Min + ImVec2(tab->Offset, 0.0f);\n    ImVec2 pos = window->DC.CursorPos;\n    ImRect bb(pos, pos + size);\n\n    // We don't have CPU clipping primitives to clip the CloseButton (until it becomes a texture), so need to add an extra draw call (temporary in the case of vertical animation)\n    const bool want_clip_rect = is_central_section && (bb.Min.x < tab_bar->ScrollingRectMinX || bb.Max.x > tab_bar->ScrollingRectMaxX);\n    if (want_clip_rect)\n        PushClipRect(ImVec2(ImMax(bb.Min.x, tab_bar->ScrollingRectMinX), bb.Min.y - 1), ImVec2(tab_bar->ScrollingRectMaxX, bb.Max.y), true);\n\n    ImVec2 backup_cursor_max_pos = window->DC.CursorMaxPos;\n    ItemSize(bb.GetSize(), style.FramePadding.y);\n    window->DC.CursorMaxPos = backup_cursor_max_pos;\n\n    if (!ItemAdd(bb, id))\n    {\n        if (want_clip_rect)\n            PopClipRect();\n        window->DC.CursorPos = backup_main_cursor_pos;\n        return tab_contents_visible;\n    }\n\n    // Click to Select a tab\n    // Allow the close button to overlap\n    ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap);\n    if (g.DragDropActive)\n        button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;\n    bool hovered, held;\n    bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);\n    if (pressed && !is_tab_button)\n        TabBarQueueFocus(tab_bar, tab);\n\n    // Drag and drop: re-order tabs\n    if (held && !tab_appearing && IsMouseDragging(0))\n    {\n        if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable))\n        {\n            // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x\n            if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x)\n            {\n                TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos);\n            }\n            else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x)\n            {\n                TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos);\n            }\n        }\n    }\n\n#if 0\n    if (hovered && g.HoveredIdNotActiveTimer > TOOLTIP_DELAY && bb.GetWidth() < tab->ContentWidth)\n    {\n        // Enlarge tab display when hovering\n        bb.Max.x = bb.Min.x + IM_TRUNC(ImLerp(bb.GetWidth(), tab->ContentWidth, ImSaturate((g.HoveredIdNotActiveTimer - 0.40f) * 6.0f)));\n        display_draw_list = GetForegroundDrawList(window);\n        TabItemBackground(display_draw_list, bb, flags, GetColorU32(ImGuiCol_TitleBgActive));\n    }\n#endif\n\n    // Render tab shape\n    ImDrawList* display_draw_list = window->DrawList;\n    const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabActive : ImGuiCol_TabUnfocusedActive) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabUnfocused));\n    TabItemBackground(display_draw_list, bb, flags, tab_col);\n    RenderNavHighlight(bb, id);\n\n    // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget.\n    const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);\n    if (hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button)\n        TabBarQueueFocus(tab_bar, tab);\n\n    if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)\n        flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton;\n\n    // Render tab label, process close button\n    const ImGuiID close_button_id = p_open ? GetIDWithSeed(\"#CLOSE\", NULL, id) : 0;\n    bool just_closed;\n    bool text_clipped;\n    TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped);\n    if (just_closed && p_open != NULL)\n    {\n        *p_open = false;\n        TabBarCloseTab(tab_bar, tab);\n    }\n\n    // Restore main window position so user can draw there\n    if (want_clip_rect)\n        PopClipRect();\n    window->DC.CursorPos = backup_main_cursor_pos;\n\n    // Tooltip\n    // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok)\n    // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores)\n    // FIXME: This is a mess.\n    // FIXME: We may want disabled tab to still display the tooltip?\n    if (text_clipped && g.HoveredId == id && !held)\n        if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip))\n            SetItemTooltip(\"%.*s\", (int)(FindRenderedTextEnd(label) - label), label);\n\n    IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected\n    if (is_tab_button)\n        return pressed;\n    return tab_contents_visible;\n}\n\n// [Public] This is call is 100% optional but it allows to remove some one-frame glitches when a tab has been unexpectedly removed.\n// To use it to need to call the function SetTabItemClosed() between BeginTabBar() and EndTabBar().\n// Tabs closed by the close button will automatically be flagged to avoid this issue.\nvoid    ImGui::SetTabItemClosed(const char* label)\n{\n    ImGuiContext& g = *GImGui;\n    bool is_within_manual_tab_bar = g.CurrentTabBar && !(g.CurrentTabBar->Flags & ImGuiTabBarFlags_DockNode);\n    if (is_within_manual_tab_bar)\n    {\n        ImGuiTabBar* tab_bar = g.CurrentTabBar;\n        ImGuiID tab_id = TabBarCalcTabID(tab_bar, label, NULL);\n        if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id))\n            tab->WantClose = true; // Will be processed by next call to TabBarLayout()\n    }\n}\n\nImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker)\n{\n    ImGuiContext& g = *GImGui;\n    ImVec2 label_size = CalcTextSize(label, NULL, true);\n    ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f);\n    if (has_close_button_or_unsaved_marker)\n        size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle.\n    else\n        size.x += g.Style.FramePadding.x + 1.0f;\n    return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y);\n}\n\nImVec2 ImGui::TabItemCalcSize(ImGuiWindow*)\n{\n    IM_ASSERT(0); // This function exists to facilitate merge with 'docking' branch.\n    return ImVec2(0.0f, 0.0f);\n}\n\nvoid ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col)\n{\n    // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking \"detached\" from it.\n    ImGuiContext& g = *GImGui;\n    const float width = bb.GetWidth();\n    IM_UNUSED(flags);\n    IM_ASSERT(width > 0.0f);\n    const float rounding = ImMax(0.0f, ImMin((flags & ImGuiTabItemFlags_Button) ? g.Style.FrameRounding : g.Style.TabRounding, width * 0.5f - 1.0f));\n    const float y1 = bb.Min.y + 1.0f;\n    const float y2 = bb.Max.y - g.Style.TabBarBorderSize;\n    draw_list->PathLineTo(ImVec2(bb.Min.x, y2));\n    draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding, y1 + rounding), rounding, 6, 9);\n    draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding, y1 + rounding), rounding, 9, 12);\n    draw_list->PathLineTo(ImVec2(bb.Max.x, y2));\n    draw_list->PathFillConvex(col);\n    if (g.Style.TabBorderSize > 0.0f)\n    {\n        draw_list->PathLineTo(ImVec2(bb.Min.x + 0.5f, y2));\n        draw_list->PathArcToFast(ImVec2(bb.Min.x + rounding + 0.5f, y1 + rounding + 0.5f), rounding, 6, 9);\n        draw_list->PathArcToFast(ImVec2(bb.Max.x - rounding - 0.5f, y1 + rounding + 0.5f), rounding, 9, 12);\n        draw_list->PathLineTo(ImVec2(bb.Max.x - 0.5f, y2));\n        draw_list->PathStroke(GetColorU32(ImGuiCol_Border), 0, g.Style.TabBorderSize);\n    }\n}\n\n// Render text label (with custom clipping) + Unsaved Document marker + Close Button logic\n// We tend to lock style.FramePadding for a given tab-bar, hence the 'frame_padding' parameter.\nvoid ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped)\n{\n    ImGuiContext& g = *GImGui;\n    ImVec2 label_size = CalcTextSize(label, NULL, true);\n\n    if (out_just_closed)\n        *out_just_closed = false;\n    if (out_text_clipped)\n        *out_text_clipped = false;\n\n    if (bb.GetWidth() <= 1.0f)\n        return;\n\n    // In Style V2 we'll have full override of all colors per state (e.g. focused, selected)\n    // But right now if you want to alter text color of tabs this is what you need to do.\n#if 0\n    const float backup_alpha = g.Style.Alpha;\n    if (!is_contents_visible)\n        g.Style.Alpha *= 0.7f;\n#endif\n\n    // Render text label (with clipping + alpha gradient) + unsaved marker\n    ImRect text_pixel_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);\n    ImRect text_ellipsis_clip_bb = text_pixel_clip_bb;\n\n    // Return clipped state ignoring the close button\n    if (out_text_clipped)\n    {\n        *out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_pixel_clip_bb.Max.x;\n        //draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255));\n    }\n\n    const float button_sz = g.FontSize;\n    const ImVec2 button_pos(ImMax(bb.Min.x, bb.Max.x - frame_padding.x - button_sz), bb.Min.y + frame_padding.y);\n\n    // Close Button & Unsaved Marker\n    // We are relying on a subtle and confusing distinction between 'hovered' and 'g.HoveredId' which happens because we are using ImGuiButtonFlags_AllowOverlapMode + SetItemAllowOverlap()\n    //  'hovered' will be true when hovering the Tab but NOT when hovering the close button\n    //  'g.HoveredId==id' will be true when hovering the Tab including when hovering the close button\n    //  'g.ActiveId==close_button_id' will be true when we are holding on the close button, in which case both hovered booleans are false\n    bool close_button_pressed = false;\n    bool close_button_visible = false;\n    if (close_button_id != 0)\n        if (is_contents_visible || bb.GetWidth() >= ImMax(button_sz, g.Style.TabMinWidthForCloseButton))\n            if (g.HoveredId == tab_id || g.HoveredId == close_button_id || g.ActiveId == tab_id || g.ActiveId == close_button_id)\n                close_button_visible = true;\n    bool unsaved_marker_visible = (flags & ImGuiTabItemFlags_UnsavedDocument) != 0 && (button_pos.x + button_sz <= bb.Max.x);\n\n    if (close_button_visible)\n    {\n        ImGuiLastItemData last_item_backup = g.LastItemData;\n        if (CloseButton(close_button_id, button_pos))\n            close_button_pressed = true;\n        g.LastItemData = last_item_backup;\n\n        // Close with middle mouse button\n        if (!(flags & ImGuiTabItemFlags_NoCloseWithMiddleMouseButton) && IsMouseClicked(2))\n            close_button_pressed = true;\n    }\n    else if (unsaved_marker_visible)\n    {\n        const ImRect bullet_bb(button_pos, button_pos + ImVec2(button_sz, button_sz));\n        RenderBullet(draw_list, bullet_bb.GetCenter(), GetColorU32(ImGuiCol_Text));\n    }\n\n    // This is all rather complicated\n    // (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position)\n    // FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist..\n    float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb.Max.x : bb.Max.x - 1.0f;\n    if (close_button_visible || unsaved_marker_visible)\n    {\n        text_pixel_clip_bb.Max.x -= close_button_visible ? (button_sz) : (button_sz * 0.80f);\n        text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f;\n        ellipsis_max_x = text_pixel_clip_bb.Max.x;\n    }\n    RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb.Max.x, ellipsis_max_x, label, NULL, &label_size);\n\n#if 0\n    if (!is_contents_visible)\n        g.Style.Alpha = backup_alpha;\n#endif\n\n    if (out_just_closed)\n        *out_just_closed = close_button_pressed;\n}\n\n\n#endif // #ifndef IMGUI_DISABLE\n"
  },
  {
    "path": "third-party/imgui/imstb_rectpack.h",
    "content": "// [DEAR IMGUI]\n// This is a slightly modified version of stb_rect_pack.h 1.01.\n// Grep for [DEAR IMGUI] to find the changes.\n// \n// stb_rect_pack.h - v1.01 - public domain - rectangle packing\n// Sean Barrett 2014\n//\n// Useful for e.g. packing rectangular textures into an atlas.\n// Does not do rotation.\n//\n// Before #including,\n//\n//    #define STB_RECT_PACK_IMPLEMENTATION\n//\n// in the file that you want to have the implementation.\n//\n// Not necessarily the awesomest packing method, but better than\n// the totally naive one in stb_truetype (which is primarily what\n// this is meant to replace).\n//\n// Has only had a few tests run, may have issues.\n//\n// More docs to come.\n//\n// No memory allocations; uses qsort() and assert() from stdlib.\n// Can override those by defining STBRP_SORT and STBRP_ASSERT.\n//\n// This library currently uses the Skyline Bottom-Left algorithm.\n//\n// Please note: better rectangle packers are welcome! Please\n// implement them to the same API, but with a different init\n// function.\n//\n// Credits\n//\n//  Library\n//    Sean Barrett\n//  Minor features\n//    Martins Mozeiko\n//    github:IntellectualKitty\n//\n//  Bugfixes / warning fixes\n//    Jeremy Jaussaud\n//    Fabian Giesen\n//\n// Version history:\n//\n//     1.01  (2021-07-11)  always use large rect mode, expose STBRP__MAXVAL in public section\n//     1.00  (2019-02-25)  avoid small space waste; gracefully fail too-wide rectangles\n//     0.99  (2019-02-07)  warning fixes\n//     0.11  (2017-03-03)  return packing success/fail result\n//     0.10  (2016-10-25)  remove cast-away-const to avoid warnings\n//     0.09  (2016-08-27)  fix compiler warnings\n//     0.08  (2015-09-13)  really fix bug with empty rects (w=0 or h=0)\n//     0.07  (2015-09-13)  fix bug with empty rects (w=0 or h=0)\n//     0.06  (2015-04-15)  added STBRP_SORT to allow replacing qsort\n//     0.05:  added STBRP_ASSERT to allow replacing assert\n//     0.04:  fixed minor bug in STBRP_LARGE_RECTS support\n//     0.01:  initial release\n//\n// LICENSE\n//\n//   See end of file for license information.\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//       INCLUDE SECTION\n//\n\n#ifndef STB_INCLUDE_STB_RECT_PACK_H\n#define STB_INCLUDE_STB_RECT_PACK_H\n\n#define STB_RECT_PACK_VERSION  1\n\n#ifdef STBRP_STATIC\n#define STBRP_DEF static\n#else\n#define STBRP_DEF extern\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct stbrp_context stbrp_context;\ntypedef struct stbrp_node    stbrp_node;\ntypedef struct stbrp_rect    stbrp_rect;\n\ntypedef int            stbrp_coord;\n\n#define STBRP__MAXVAL  0x7fffffff\n// Mostly for internal use, but this is the maximum supported coordinate value.\n\nSTBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);\n// Assign packed locations to rectangles. The rectangles are of type\n// 'stbrp_rect' defined below, stored in the array 'rects', and there\n// are 'num_rects' many of them.\n//\n// Rectangles which are successfully packed have the 'was_packed' flag\n// set to a non-zero value and 'x' and 'y' store the minimum location\n// on each axis (i.e. bottom-left in cartesian coordinates, top-left\n// if you imagine y increasing downwards). Rectangles which do not fit\n// have the 'was_packed' flag set to 0.\n//\n// You should not try to access the 'rects' array from another thread\n// while this function is running, as the function temporarily reorders\n// the array while it executes.\n//\n// To pack into another rectangle, you need to call stbrp_init_target\n// again. To continue packing into the same rectangle, you can call\n// this function again. Calling this multiple times with multiple rect\n// arrays will probably produce worse packing results than calling it\n// a single time with the full rectangle array, but the option is\n// available.\n//\n// The function returns 1 if all of the rectangles were successfully\n// packed and 0 otherwise.\n\nstruct stbrp_rect\n{\n   // reserved for your use:\n   int            id;\n\n   // input:\n   stbrp_coord    w, h;\n\n   // output:\n   stbrp_coord    x, y;\n   int            was_packed;  // non-zero if valid packing\n\n}; // 16 bytes, nominally\n\n\nSTBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);\n// Initialize a rectangle packer to:\n//    pack a rectangle that is 'width' by 'height' in dimensions\n//    using temporary storage provided by the array 'nodes', which is 'num_nodes' long\n//\n// You must call this function every time you start packing into a new target.\n//\n// There is no \"shutdown\" function. The 'nodes' memory must stay valid for\n// the following stbrp_pack_rects() call (or calls), but can be freed after\n// the call (or calls) finish.\n//\n// Note: to guarantee best results, either:\n//       1. make sure 'num_nodes' >= 'width'\n//   or  2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'\n//\n// If you don't do either of the above things, widths will be quantized to multiples\n// of small integers to guarantee the algorithm doesn't run out of temporary storage.\n//\n// If you do #2, then the non-quantized algorithm will be used, but the algorithm\n// may run out of temporary storage and be unable to pack some rectangles.\n\nSTBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);\n// Optionally call this function after init but before doing any packing to\n// change the handling of the out-of-temp-memory scenario, described above.\n// If you call init again, this will be reset to the default (false).\n\n\nSTBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);\n// Optionally select which packing heuristic the library should use. Different\n// heuristics will produce better/worse results for different data sets.\n// If you call init again, this will be reset to the default.\n\nenum\n{\n   STBRP_HEURISTIC_Skyline_default=0,\n   STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,\n   STBRP_HEURISTIC_Skyline_BF_sortHeight\n};\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// the details of the following structures don't matter to you, but they must\n// be visible so you can handle the memory allocations for them\n\nstruct stbrp_node\n{\n   stbrp_coord  x,y;\n   stbrp_node  *next;\n};\n\nstruct stbrp_context\n{\n   int width;\n   int height;\n   int align;\n   int init_mode;\n   int heuristic;\n   int num_nodes;\n   stbrp_node *active_head;\n   stbrp_node *free_head;\n   stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'\n};\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//     IMPLEMENTATION SECTION\n//\n\n#ifdef STB_RECT_PACK_IMPLEMENTATION\n#ifndef STBRP_SORT\n#include <stdlib.h>\n#define STBRP_SORT qsort\n#endif\n\n#ifndef STBRP_ASSERT\n#include <assert.h>\n#define STBRP_ASSERT assert\n#endif\n\n#ifdef _MSC_VER\n#define STBRP__NOTUSED(v)  (void)(v)\n#define STBRP__CDECL       __cdecl\n#else\n#define STBRP__NOTUSED(v)  (void)sizeof(v)\n#define STBRP__CDECL\n#endif\n\nenum\n{\n   STBRP__INIT_skyline = 1\n};\n\nSTBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)\n{\n   switch (context->init_mode) {\n      case STBRP__INIT_skyline:\n         STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);\n         context->heuristic = heuristic;\n         break;\n      default:\n         STBRP_ASSERT(0);\n   }\n}\n\nSTBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)\n{\n   if (allow_out_of_mem)\n      // if it's ok to run out of memory, then don't bother aligning them;\n      // this gives better packing, but may fail due to OOM (even though\n      // the rectangles easily fit). @TODO a smarter approach would be to only\n      // quantize once we've hit OOM, then we could get rid of this parameter.\n      context->align = 1;\n   else {\n      // if it's not ok to run out of memory, then quantize the widths\n      // so that num_nodes is always enough nodes.\n      //\n      // I.e. num_nodes * align >= width\n      //                  align >= width / num_nodes\n      //                  align = ceil(width/num_nodes)\n\n      context->align = (context->width + context->num_nodes-1) / context->num_nodes;\n   }\n}\n\nSTBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)\n{\n   int i;\n\n   for (i=0; i < num_nodes-1; ++i)\n      nodes[i].next = &nodes[i+1];\n   nodes[i].next = NULL;\n   context->init_mode = STBRP__INIT_skyline;\n   context->heuristic = STBRP_HEURISTIC_Skyline_default;\n   context->free_head = &nodes[0];\n   context->active_head = &context->extra[0];\n   context->width = width;\n   context->height = height;\n   context->num_nodes = num_nodes;\n   stbrp_setup_allow_out_of_mem(context, 0);\n\n   // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)\n   context->extra[0].x = 0;\n   context->extra[0].y = 0;\n   context->extra[0].next = &context->extra[1];\n   context->extra[1].x = (stbrp_coord) width;\n   context->extra[1].y = (1<<30);\n   context->extra[1].next = NULL;\n}\n\n// find minimum y position if it starts at x1\nstatic int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)\n{\n   stbrp_node *node = first;\n   int x1 = x0 + width;\n   int min_y, visited_width, waste_area;\n\n   STBRP__NOTUSED(c);\n\n   STBRP_ASSERT(first->x <= x0);\n\n   #if 0\n   // skip in case we're past the node\n   while (node->next->x <= x0)\n      ++node;\n   #else\n   STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency\n   #endif\n\n   STBRP_ASSERT(node->x <= x0);\n\n   min_y = 0;\n   waste_area = 0;\n   visited_width = 0;\n   while (node->x < x1) {\n      if (node->y > min_y) {\n         // raise min_y higher.\n         // we've accounted for all waste up to min_y,\n         // but we'll now add more waste for everything we've visted\n         waste_area += visited_width * (node->y - min_y);\n         min_y = node->y;\n         // the first time through, visited_width might be reduced\n         if (node->x < x0)\n            visited_width += node->next->x - x0;\n         else\n            visited_width += node->next->x - node->x;\n      } else {\n         // add waste area\n         int under_width = node->next->x - node->x;\n         if (under_width + visited_width > width)\n            under_width = width - visited_width;\n         waste_area += under_width * (min_y - node->y);\n         visited_width += under_width;\n      }\n      node = node->next;\n   }\n\n   *pwaste = waste_area;\n   return min_y;\n}\n\ntypedef struct\n{\n   int x,y;\n   stbrp_node **prev_link;\n} stbrp__findresult;\n\nstatic stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)\n{\n   int best_waste = (1<<30), best_x, best_y = (1 << 30);\n   stbrp__findresult fr;\n   stbrp_node **prev, *node, *tail, **best = NULL;\n\n   // align to multiple of c->align\n   width = (width + c->align - 1);\n   width -= width % c->align;\n   STBRP_ASSERT(width % c->align == 0);\n\n   // if it can't possibly fit, bail immediately\n   if (width > c->width || height > c->height) {\n      fr.prev_link = NULL;\n      fr.x = fr.y = 0;\n      return fr;\n   }\n\n   node = c->active_head;\n   prev = &c->active_head;\n   while (node->x + width <= c->width) {\n      int y,waste;\n      y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);\n      if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL\n         // bottom left\n         if (y < best_y) {\n            best_y = y;\n            best = prev;\n         }\n      } else {\n         // best-fit\n         if (y + height <= c->height) {\n            // can only use it if it first vertically\n            if (y < best_y || (y == best_y && waste < best_waste)) {\n               best_y = y;\n               best_waste = waste;\n               best = prev;\n            }\n         }\n      }\n      prev = &node->next;\n      node = node->next;\n   }\n\n   best_x = (best == NULL) ? 0 : (*best)->x;\n\n   // if doing best-fit (BF), we also have to try aligning right edge to each node position\n   //\n   // e.g, if fitting\n   //\n   //     ____________________\n   //    |____________________|\n   //\n   //            into\n   //\n   //   |                         |\n   //   |             ____________|\n   //   |____________|\n   //\n   // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned\n   //\n   // This makes BF take about 2x the time\n\n   if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {\n      tail = c->active_head;\n      node = c->active_head;\n      prev = &c->active_head;\n      // find first node that's admissible\n      while (tail->x < width)\n         tail = tail->next;\n      while (tail) {\n         int xpos = tail->x - width;\n         int y,waste;\n         STBRP_ASSERT(xpos >= 0);\n         // find the left position that matches this\n         while (node->next->x <= xpos) {\n            prev = &node->next;\n            node = node->next;\n         }\n         STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);\n         y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);\n         if (y + height <= c->height) {\n            if (y <= best_y) {\n               if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {\n                  best_x = xpos;\n                  //STBRP_ASSERT(y <= best_y); [DEAR IMGUI]\n                  best_y = y;\n                  best_waste = waste;\n                  best = prev;\n               }\n            }\n         }\n         tail = tail->next;\n      }\n   }\n\n   fr.prev_link = best;\n   fr.x = best_x;\n   fr.y = best_y;\n   return fr;\n}\n\nstatic stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)\n{\n   // find best position according to heuristic\n   stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);\n   stbrp_node *node, *cur;\n\n   // bail if:\n   //    1. it failed\n   //    2. the best node doesn't fit (we don't always check this)\n   //    3. we're out of memory\n   if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {\n      res.prev_link = NULL;\n      return res;\n   }\n\n   // on success, create new node\n   node = context->free_head;\n   node->x = (stbrp_coord) res.x;\n   node->y = (stbrp_coord) (res.y + height);\n\n   context->free_head = node->next;\n\n   // insert the new node into the right starting point, and\n   // let 'cur' point to the remaining nodes needing to be\n   // stiched back in\n\n   cur = *res.prev_link;\n   if (cur->x < res.x) {\n      // preserve the existing one, so start testing with the next one\n      stbrp_node *next = cur->next;\n      cur->next = node;\n      cur = next;\n   } else {\n      *res.prev_link = node;\n   }\n\n   // from here, traverse cur and free the nodes, until we get to one\n   // that shouldn't be freed\n   while (cur->next && cur->next->x <= res.x + width) {\n      stbrp_node *next = cur->next;\n      // move the current node to the free list\n      cur->next = context->free_head;\n      context->free_head = cur;\n      cur = next;\n   }\n\n   // stitch the list back in\n   node->next = cur;\n\n   if (cur->x < res.x + width)\n      cur->x = (stbrp_coord) (res.x + width);\n\n#ifdef _DEBUG\n   cur = context->active_head;\n   while (cur->x < context->width) {\n      STBRP_ASSERT(cur->x < cur->next->x);\n      cur = cur->next;\n   }\n   STBRP_ASSERT(cur->next == NULL);\n\n   {\n      int count=0;\n      cur = context->active_head;\n      while (cur) {\n         cur = cur->next;\n         ++count;\n      }\n      cur = context->free_head;\n      while (cur) {\n         cur = cur->next;\n         ++count;\n      }\n      STBRP_ASSERT(count == context->num_nodes+2);\n   }\n#endif\n\n   return res;\n}\n\nstatic int STBRP__CDECL rect_height_compare(const void *a, const void *b)\n{\n   const stbrp_rect *p = (const stbrp_rect *) a;\n   const stbrp_rect *q = (const stbrp_rect *) b;\n   if (p->h > q->h)\n      return -1;\n   if (p->h < q->h)\n      return  1;\n   return (p->w > q->w) ? -1 : (p->w < q->w);\n}\n\nstatic int STBRP__CDECL rect_original_order(const void *a, const void *b)\n{\n   const stbrp_rect *p = (const stbrp_rect *) a;\n   const stbrp_rect *q = (const stbrp_rect *) b;\n   return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);\n}\n\nSTBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)\n{\n   int i, all_rects_packed = 1;\n\n   // we use the 'was_packed' field internally to allow sorting/unsorting\n   for (i=0; i < num_rects; ++i) {\n      rects[i].was_packed = i;\n   }\n\n   // sort according to heuristic\n   STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);\n\n   for (i=0; i < num_rects; ++i) {\n      if (rects[i].w == 0 || rects[i].h == 0) {\n         rects[i].x = rects[i].y = 0;  // empty rect needs no space\n      } else {\n         stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);\n         if (fr.prev_link) {\n            rects[i].x = (stbrp_coord) fr.x;\n            rects[i].y = (stbrp_coord) fr.y;\n         } else {\n            rects[i].x = rects[i].y = STBRP__MAXVAL;\n         }\n      }\n   }\n\n   // unsort\n   STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);\n\n   // set was_packed flags and all_rects_packed status\n   for (i=0; i < num_rects; ++i) {\n      rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);\n      if (!rects[i].was_packed)\n         all_rects_packed = 0;\n   }\n\n   // return the all_rects_packed status\n   return all_rects_packed;\n}\n#endif\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\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------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "third-party/imgui/imstb_textedit.h",
    "content": "// [DEAR IMGUI]\n// This is a slightly modified version of stb_textedit.h 1.14.\n// Those changes would need to be pushed into nothings/stb:\n// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)\n// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)\n// Grep for [DEAR IMGUI] to find the changes.\n\n// stb_textedit.h - v1.14  - public domain - Sean Barrett\n// Development of this library was sponsored by RAD Game Tools\n//\n// This C header file implements the guts of a multi-line text-editing\n// widget; you implement display, word-wrapping, and low-level string\n// insertion/deletion, and stb_textedit will map user inputs into\n// insertions & deletions, plus updates to the cursor position,\n// selection state, and undo state.\n//\n// It is intended for use in games and other systems that need to build\n// their own custom widgets and which do not have heavy text-editing\n// requirements (this library is not recommended for use for editing large\n// texts, as its performance does not scale and it has limited undo).\n//\n// Non-trivial behaviors are modelled after Windows text controls.\n//\n//\n// LICENSE\n//\n// See end of file for license information.\n//\n//\n// DEPENDENCIES\n//\n// Uses the C runtime function 'memmove', which you can override\n// by defining STB_TEXTEDIT_memmove before the implementation.\n// Uses no other functions. Performs no runtime allocations.\n//\n//\n// VERSION HISTORY\n//\n//   1.14 (2021-07-11) page up/down, various fixes\n//   1.13 (2019-02-07) fix bug in undo size management\n//   1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash\n//   1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield\n//   1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual\n//   1.9  (2016-08-27) customizable move-by-word\n//   1.8  (2016-04-02) better keyboard handling when mouse button is down\n//   1.7  (2015-09-13) change y range handling in case baseline is non-0\n//   1.6  (2015-04-15) allow STB_TEXTEDIT_memmove\n//   1.5  (2014-09-10) add support for secondary keys for OS X\n//   1.4  (2014-08-17) fix signed/unsigned warnings\n//   1.3  (2014-06-19) fix mouse clicking to round to nearest char boundary\n//   1.2  (2014-05-27) fix some RAD types that had crept into the new code\n//   1.1  (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE )\n//   1.0  (2012-07-26) improve documentation, initial public release\n//   0.3  (2012-02-24) bugfixes, single-line mode; insert mode\n//   0.2  (2011-11-28) fixes to undo/redo\n//   0.1  (2010-07-08) initial version\n//\n// ADDITIONAL CONTRIBUTORS\n//\n//   Ulf Winklemann: move-by-word in 1.1\n//   Fabian Giesen: secondary key inputs in 1.5\n//   Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6\n//   Louis Schnellbach: page up/down in 1.14\n//\n//   Bugfixes:\n//      Scott Graham\n//      Daniel Keller\n//      Omar Cornut\n//      Dan Thompson\n//\n// USAGE\n//\n// This file behaves differently depending on what symbols you define\n// before including it.\n//\n//\n// Header-file mode:\n//\n//   If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this,\n//   it will operate in \"header file\" mode. In this mode, it declares a\n//   single public symbol, STB_TexteditState, which encapsulates the current\n//   state of a text widget (except for the string, which you will store\n//   separately).\n//\n//   To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a\n//   primitive type that defines a single character (e.g. char, wchar_t, etc).\n//\n//   To save space or increase undo-ability, you can optionally define the\n//   following things that are used by the undo system:\n//\n//      STB_TEXTEDIT_POSITIONTYPE         small int type encoding a valid cursor position\n//      STB_TEXTEDIT_UNDOSTATECOUNT       the number of undo states to allow\n//      STB_TEXTEDIT_UNDOCHARCOUNT        the number of characters to store in the undo buffer\n//\n//   If you don't define these, they are set to permissive types and\n//   moderate sizes. The undo system does no memory allocations, so\n//   it grows STB_TexteditState by the worst-case storage which is (in bytes):\n//\n//        [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATECOUNT\n//      +          sizeof(STB_TEXTEDIT_CHARTYPE)      * STB_TEXTEDIT_UNDOCHARCOUNT\n//\n//\n// Implementation mode:\n//\n//   If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it\n//   will compile the implementation of the text edit widget, depending\n//   on a large number of symbols which must be defined before the include.\n//\n//   The implementation is defined only as static functions. You will then\n//   need to provide your own APIs in the same file which will access the\n//   static functions.\n//\n//   The basic concept is that you provide a \"string\" object which\n//   behaves like an array of characters. stb_textedit uses indices to\n//   refer to positions in the string, implicitly representing positions\n//   in the displayed textedit. This is true for both plain text and\n//   rich text; even with rich text stb_truetype interacts with your\n//   code as if there was an array of all the displayed characters.\n//\n// Symbols that must be the same in header-file and implementation mode:\n//\n//     STB_TEXTEDIT_CHARTYPE             the character type\n//     STB_TEXTEDIT_POSITIONTYPE         small type that is a valid cursor position\n//     STB_TEXTEDIT_UNDOSTATECOUNT       the number of undo states to allow\n//     STB_TEXTEDIT_UNDOCHARCOUNT        the number of characters to store in the undo buffer\n//\n// Symbols you must define for implementation mode:\n//\n//    STB_TEXTEDIT_STRING               the type of object representing a string being edited,\n//                                      typically this is a wrapper object with other data you need\n//\n//    STB_TEXTEDIT_STRINGLEN(obj)       the length of the string (ideally O(1))\n//    STB_TEXTEDIT_LAYOUTROW(&r,obj,n)  returns the results of laying out a line of characters\n//                                        starting from character #n (see discussion below)\n//    STB_TEXTEDIT_GETWIDTH(obj,n,i)    returns the pixel delta from the xpos of the i'th character\n//                                        to the xpos of the i+1'th char for a line of characters\n//                                        starting at character #n (i.e. accounts for kerning\n//                                        with previous char)\n//    STB_TEXTEDIT_KEYTOTEXT(k)         maps a keyboard input to an insertable character\n//                                        (return type is int, -1 means not valid to insert)\n//    STB_TEXTEDIT_GETCHAR(obj,i)       returns the i'th character of obj, 0-based\n//    STB_TEXTEDIT_NEWLINE              the character returned by _GETCHAR() we recognize\n//                                        as manually wordwrapping for end-of-line positioning\n//\n//    STB_TEXTEDIT_DELETECHARS(obj,i,n)      delete n characters starting at i\n//    STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n)   insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*)\n//\n//    STB_TEXTEDIT_K_SHIFT       a power of two that is or'd in to a keyboard input to represent the shift key\n//\n//    STB_TEXTEDIT_K_LEFT        keyboard input to move cursor left\n//    STB_TEXTEDIT_K_RIGHT       keyboard input to move cursor right\n//    STB_TEXTEDIT_K_UP          keyboard input to move cursor up\n//    STB_TEXTEDIT_K_DOWN        keyboard input to move cursor down\n//    STB_TEXTEDIT_K_PGUP        keyboard input to move cursor up a page\n//    STB_TEXTEDIT_K_PGDOWN      keyboard input to move cursor down a page\n//    STB_TEXTEDIT_K_LINESTART   keyboard input to move cursor to start of line  // e.g. HOME\n//    STB_TEXTEDIT_K_LINEEND     keyboard input to move cursor to end of line    // e.g. END\n//    STB_TEXTEDIT_K_TEXTSTART   keyboard input to move cursor to start of text  // e.g. ctrl-HOME\n//    STB_TEXTEDIT_K_TEXTEND     keyboard input to move cursor to end of text    // e.g. ctrl-END\n//    STB_TEXTEDIT_K_DELETE      keyboard input to delete selection or character under cursor\n//    STB_TEXTEDIT_K_BACKSPACE   keyboard input to delete selection or character left of cursor\n//    STB_TEXTEDIT_K_UNDO        keyboard input to perform undo\n//    STB_TEXTEDIT_K_REDO        keyboard input to perform redo\n//\n// Optional:\n//    STB_TEXTEDIT_K_INSERT              keyboard input to toggle insert mode\n//    STB_TEXTEDIT_IS_SPACE(ch)          true if character is whitespace (e.g. 'isspace'),\n//                                          required for default WORDLEFT/WORDRIGHT handlers\n//    STB_TEXTEDIT_MOVEWORDLEFT(obj,i)   custom handler for WORDLEFT, returns index to move cursor to\n//    STB_TEXTEDIT_MOVEWORDRIGHT(obj,i)  custom handler for WORDRIGHT, returns index to move cursor to\n//    STB_TEXTEDIT_K_WORDLEFT            keyboard input to move cursor left one word // e.g. ctrl-LEFT\n//    STB_TEXTEDIT_K_WORDRIGHT           keyboard input to move cursor right one word // e.g. ctrl-RIGHT\n//    STB_TEXTEDIT_K_LINESTART2          secondary keyboard input to move cursor to start of line\n//    STB_TEXTEDIT_K_LINEEND2            secondary keyboard input to move cursor to end of line\n//    STB_TEXTEDIT_K_TEXTSTART2          secondary keyboard input to move cursor to start of text\n//    STB_TEXTEDIT_K_TEXTEND2            secondary keyboard input to move cursor to end of text\n//\n// Keyboard input must be encoded as a single integer value; e.g. a character code\n// and some bitflags that represent shift states. to simplify the interface, SHIFT must\n// be a bitflag, so we can test the shifted state of cursor movements to allow selection,\n// i.e. (STB_TEXTEDIT_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow.\n//\n// You can encode other things, such as CONTROL or ALT, in additional bits, and\n// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example,\n// my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN\n// bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit,\n// and I pass both WM_KEYDOWN and WM_CHAR events to the \"key\" function in the\n// API below. The control keys will only match WM_KEYDOWN events because of the\n// keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN\n// bit so it only decodes WM_CHAR events.\n//\n// STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed\n// row of characters assuming they start on the i'th character--the width and\n// the height and the number of characters consumed. This allows this library\n// to traverse the entire layout incrementally. You need to compute word-wrapping\n// here.\n//\n// Each textfield keeps its own insert mode state, which is not how normal\n// applications work. To keep an app-wide insert mode, update/copy the\n// \"insert_mode\" field of STB_TexteditState before/after calling API functions.\n//\n// API\n//\n//    void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line)\n//\n//    void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)\n//    void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)\n//    int  stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n//    int  stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)\n//    void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)\n//\n//    Each of these functions potentially updates the string and updates the\n//    state.\n//\n//      initialize_state:\n//          set the textedit state to a known good default state when initially\n//          constructing the textedit.\n//\n//      click:\n//          call this with the mouse x,y on a mouse down; it will update the cursor\n//          and reset the selection start/end to the cursor point. the x,y must\n//          be relative to the text widget, with (0,0) being the top left.\n//\n//      drag:\n//          call this with the mouse x,y on a mouse drag/up; it will update the\n//          cursor and the selection end point\n//\n//      cut:\n//          call this to delete the current selection; returns true if there was\n//          one. you should FIRST copy the current selection to the system paste buffer.\n//          (To copy, just copy the current selection out of the string yourself.)\n//\n//      paste:\n//          call this to paste text at the current cursor point or over the current\n//          selection if there is one.\n//\n//      key:\n//          call this for keyboard inputs sent to the textfield. you can use it\n//          for \"key down\" events or for \"translated\" key events. if you need to\n//          do both (as in Win32), or distinguish Unicode characters from control\n//          inputs, set a high bit to distinguish the two; then you can define the\n//          various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit\n//          set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is\n//          clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to\n//          anything other type you wante before including.\n//\n//\n//   When rendering, you can read the cursor position and selection state from\n//   the STB_TexteditState.\n//\n//\n// Notes:\n//\n// This is designed to be usable in IMGUI, so it allows for the possibility of\n// running in an IMGUI that has NOT cached the multi-line layout. For this\n// reason, it provides an interface that is compatible with computing the\n// layout incrementally--we try to make sure we make as few passes through\n// as possible. (For example, to locate the mouse pointer in the text, we\n// could define functions that return the X and Y positions of characters\n// and binary search Y and then X, but if we're doing dynamic layout this\n// will run the layout algorithm many times, so instead we manually search\n// forward in one pass. Similar logic applies to e.g. up-arrow and\n// down-arrow movement.)\n//\n// If it's run in a widget that *has* cached the layout, then this is less\n// efficient, but it's not horrible on modern computers. But you wouldn't\n// want to edit million-line files with it.\n\n\n////////////////////////////////////////////////////////////////////////////\n////////////////////////////////////////////////////////////////////////////\n////\n////   Header-file mode\n////\n////\n\n#ifndef INCLUDE_STB_TEXTEDIT_H\n#define INCLUDE_STB_TEXTEDIT_H\n\n////////////////////////////////////////////////////////////////////////\n//\n//     STB_TexteditState\n//\n// Definition of STB_TexteditState which you should store\n// per-textfield; it includes cursor position, selection state,\n// and undo state.\n//\n\n#ifndef STB_TEXTEDIT_UNDOSTATECOUNT\n#define STB_TEXTEDIT_UNDOSTATECOUNT   99\n#endif\n#ifndef STB_TEXTEDIT_UNDOCHARCOUNT\n#define STB_TEXTEDIT_UNDOCHARCOUNT   999\n#endif\n#ifndef STB_TEXTEDIT_CHARTYPE\n#define STB_TEXTEDIT_CHARTYPE        int\n#endif\n#ifndef STB_TEXTEDIT_POSITIONTYPE\n#define STB_TEXTEDIT_POSITIONTYPE    int\n#endif\n\ntypedef struct\n{\n   // private data\n   STB_TEXTEDIT_POSITIONTYPE  where;\n   STB_TEXTEDIT_POSITIONTYPE  insert_length;\n   STB_TEXTEDIT_POSITIONTYPE  delete_length;\n   int                        char_storage;\n} StbUndoRecord;\n\ntypedef struct\n{\n   // private data\n   StbUndoRecord          undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT];\n   STB_TEXTEDIT_CHARTYPE  undo_char[STB_TEXTEDIT_UNDOCHARCOUNT];\n   short undo_point, redo_point;\n   int undo_char_point, redo_char_point;\n} StbUndoState;\n\ntypedef struct\n{\n   /////////////////////\n   //\n   // public data\n   //\n\n   int cursor;\n   // position of the text cursor within the string\n\n   int select_start;          // selection start point\n   int select_end;\n   // selection start and end point in characters; if equal, no selection.\n   // note that start may be less than or greater than end (e.g. when\n   // dragging the mouse, start is where the initial click was, and you\n   // can drag in either direction)\n\n   unsigned char insert_mode;\n   // each textfield keeps its own insert mode state. to keep an app-wide\n   // insert mode, copy this value in/out of the app state\n\n   int row_count_per_page;\n   // page size in number of row.\n   // this value MUST be set to >0 for pageup or pagedown in multilines documents.\n\n   /////////////////////\n   //\n   // private data\n   //\n   unsigned char cursor_at_end_of_line; // not implemented yet\n   unsigned char initialized;\n   unsigned char has_preferred_x;\n   unsigned char single_line;\n   unsigned char padding1, padding2, padding3;\n   float preferred_x; // this determines where the cursor up/down tries to seek to along x\n   StbUndoState undostate;\n} STB_TexteditState;\n\n\n////////////////////////////////////////////////////////////////////////\n//\n//     StbTexteditRow\n//\n// Result of layout query, used by stb_textedit to determine where\n// the text in each row is.\n\n// result of layout query\ntypedef struct\n{\n   float x0,x1;             // starting x location, end x location (allows for align=right, etc)\n   float baseline_y_delta;  // position of baseline relative to previous row's baseline\n   float ymin,ymax;         // height of row above and below baseline\n   int num_chars;\n} StbTexteditRow;\n#endif //INCLUDE_STB_TEXTEDIT_H\n\n\n////////////////////////////////////////////////////////////////////////////\n////////////////////////////////////////////////////////////////////////////\n////\n////   Implementation mode\n////\n////\n\n\n// implementation isn't include-guarded, since it might have indirectly\n// included just the \"header\" portion\n#ifdef STB_TEXTEDIT_IMPLEMENTATION\n\n#ifndef STB_TEXTEDIT_memmove\n#include <string.h>\n#define STB_TEXTEDIT_memmove memmove\n#endif\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n//      Mouse input handling\n//\n\n// traverse the layout to locate the nearest character to a display position\nstatic int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y)\n{\n   StbTexteditRow r;\n   int n = STB_TEXTEDIT_STRINGLEN(str);\n   float base_y = 0, prev_x;\n   int i=0, k;\n\n   r.x0 = r.x1 = 0;\n   r.ymin = r.ymax = 0;\n   r.num_chars = 0;\n\n   // search rows to find one that straddles 'y'\n   while (i < n) {\n      STB_TEXTEDIT_LAYOUTROW(&r, str, i);\n      if (r.num_chars <= 0)\n         return n;\n\n      if (i==0 && y < base_y + r.ymin)\n         return 0;\n\n      if (y < base_y + r.ymax)\n         break;\n\n      i += r.num_chars;\n      base_y += r.baseline_y_delta;\n   }\n\n   // below all text, return 'after' last character\n   if (i >= n)\n      return n;\n\n   // check if it's before the beginning of the line\n   if (x < r.x0)\n      return i;\n\n   // check if it's before the end of the line\n   if (x < r.x1) {\n      // search characters in row for one that straddles 'x'\n      prev_x = r.x0;\n      for (k=0; k < r.num_chars; ++k) {\n         float w = STB_TEXTEDIT_GETWIDTH(str, i, k);\n         if (x < prev_x+w) {\n            if (x < prev_x+w/2)\n               return k+i;\n            else\n               return k+i+1;\n         }\n         prev_x += w;\n      }\n      // shouldn't happen, but if it does, fall through to end-of-line case\n   }\n\n   // if the last character is a newline, return that. otherwise return 'after' the last character\n   if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE)\n      return i+r.num_chars-1;\n   else\n      return i+r.num_chars;\n}\n\n// API click: on mouse down, move the cursor to the clicked location, and reset the selection\nstatic void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)\n{\n   // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse\n   // goes off the top or bottom of the text\n   if( state->single_line )\n   {\n      StbTexteditRow r;\n      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);\n      y = r.ymin;\n   }\n\n   state->cursor = stb_text_locate_coord(str, x, y);\n   state->select_start = state->cursor;\n   state->select_end = state->cursor;\n   state->has_preferred_x = 0;\n}\n\n// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location\nstatic void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y)\n{\n   int p = 0;\n\n   // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse\n   // goes off the top or bottom of the text\n   if( state->single_line )\n   {\n      StbTexteditRow r;\n      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);\n      y = r.ymin;\n   }\n\n   if (state->select_start == state->select_end)\n      state->select_start = state->cursor;\n\n   p = stb_text_locate_coord(str, x, y);\n   state->cursor = state->select_end = p;\n}\n\n/////////////////////////////////////////////////////////////////////////////\n//\n//      Keyboard input handling\n//\n\n// forward declarations\nstatic void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);\nstatic void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state);\nstatic void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length);\nstatic void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length);\nstatic void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length);\n\ntypedef struct\n{\n   float x,y;    // position of n'th character\n   float height; // height of line\n   int first_char, length; // first char of row, and length\n   int prev_first;  // first char of previous row\n} StbFindState;\n\n// find the x/y location of a character, and remember info about the previous row in\n// case we get a move-up event (for page up, we'll have to rescan)\nstatic void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line)\n{\n   StbTexteditRow r;\n   int prev_start = 0;\n   int z = STB_TEXTEDIT_STRINGLEN(str);\n   int i=0, first;\n\n   if (n == z && single_line) {\n      // special case if it's at the end (may not be needed?)\n      STB_TEXTEDIT_LAYOUTROW(&r, str, 0);\n      find->y = 0;\n      find->first_char = 0;\n      find->length = z;\n      find->height = r.ymax - r.ymin;\n      find->x = r.x1;\n      return;\n   }\n\n   // search rows to find the one that straddles character n\n   find->y = 0;\n\n   for(;;) {\n      STB_TEXTEDIT_LAYOUTROW(&r, str, i);\n      if (n < i + r.num_chars)\n         break;\n      if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE)  // [DEAR IMGUI] special handling for last line\n         break;   // [DEAR IMGUI]\n      prev_start = i;\n      i += r.num_chars;\n      find->y += r.baseline_y_delta;\n      if (i == z) // [DEAR IMGUI]\n      {\n         r.num_chars = 0; // [DEAR IMGUI]\n         break;   // [DEAR IMGUI]\n      }\n   }\n\n   find->first_char = first = i;\n   find->length = r.num_chars;\n   find->height = r.ymax - r.ymin;\n   find->prev_first = prev_start;\n\n   // now scan to find xpos\n   find->x = r.x0;\n   for (i=0; first+i < n; ++i)\n      find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);\n}\n\n#define STB_TEXT_HAS_SELECTION(s)   ((s)->select_start != (s)->select_end)\n\n// make the selection/cursor state valid if client altered the string\nstatic void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   int n = STB_TEXTEDIT_STRINGLEN(str);\n   if (STB_TEXT_HAS_SELECTION(state)) {\n      if (state->select_start > n) state->select_start = n;\n      if (state->select_end   > n) state->select_end = n;\n      // if clamping forced them to be equal, move the cursor to match\n      if (state->select_start == state->select_end)\n         state->cursor = state->select_start;\n   }\n   if (state->cursor > n) state->cursor = n;\n}\n\n// delete characters while updating undo\nstatic void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len)\n{\n   stb_text_makeundo_delete(str, state, where, len);\n   STB_TEXTEDIT_DELETECHARS(str, where, len);\n   state->has_preferred_x = 0;\n}\n\n// delete the section\nstatic void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   stb_textedit_clamp(str, state);\n   if (STB_TEXT_HAS_SELECTION(state)) {\n      if (state->select_start < state->select_end) {\n         stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start);\n         state->select_end = state->cursor = state->select_start;\n      } else {\n         stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end);\n         state->select_start = state->cursor = state->select_end;\n      }\n      state->has_preferred_x = 0;\n   }\n}\n\n// canoncialize the selection so start <= end\nstatic void stb_textedit_sortselection(STB_TexteditState *state)\n{\n   if (state->select_end < state->select_start) {\n      int temp = state->select_end;\n      state->select_end = state->select_start;\n      state->select_start = temp;\n   }\n}\n\n// move cursor to first character of selection\nstatic void stb_textedit_move_to_first(STB_TexteditState *state)\n{\n   if (STB_TEXT_HAS_SELECTION(state)) {\n      stb_textedit_sortselection(state);\n      state->cursor = state->select_start;\n      state->select_end = state->select_start;\n      state->has_preferred_x = 0;\n   }\n}\n\n// move cursor to last character of selection\nstatic void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   if (STB_TEXT_HAS_SELECTION(state)) {\n      stb_textedit_sortselection(state);\n      stb_textedit_clamp(str, state);\n      state->cursor = state->select_end;\n      state->select_start = state->select_end;\n      state->has_preferred_x = 0;\n   }\n}\n\n#ifdef STB_TEXTEDIT_IS_SPACE\nstatic int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx )\n{\n   return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1;\n}\n\n#ifndef STB_TEXTEDIT_MOVEWORDLEFT\nstatic int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c )\n{\n   --c; // always move at least one character\n   while( c >= 0 && !is_word_boundary( str, c ) )\n      --c;\n\n   if( c < 0 )\n      c = 0;\n\n   return c;\n}\n#define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous\n#endif\n\n#ifndef STB_TEXTEDIT_MOVEWORDRIGHT\nstatic int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c )\n{\n   const int len = STB_TEXTEDIT_STRINGLEN(str);\n   ++c; // always move at least one character\n   while( c < len && !is_word_boundary( str, c ) )\n      ++c;\n\n   if( c > len )\n      c = len;\n\n   return c;\n}\n#define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next\n#endif\n\n#endif\n\n// update selection and cursor to match each other\nstatic void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state)\n{\n   if (!STB_TEXT_HAS_SELECTION(state))\n      state->select_start = state->select_end = state->cursor;\n   else\n      state->cursor = state->select_end;\n}\n\n// API cut: delete selection\nstatic int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   if (STB_TEXT_HAS_SELECTION(state)) {\n      stb_textedit_delete_selection(str,state); // implicitly clamps\n      state->has_preferred_x = 0;\n      return 1;\n   }\n   return 0;\n}\n\n// API paste: replace existing selection with passed-in text\nstatic int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)\n{\n   // if there's a selection, the paste should delete it\n   stb_textedit_clamp(str, state);\n   stb_textedit_delete_selection(str,state);\n   // try to insert the characters\n   if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) {\n      stb_text_makeundo_insert(state, state->cursor, len);\n      state->cursor += len;\n      state->has_preferred_x = 0;\n      return 1;\n   }\n   // note: paste failure will leave deleted selection, may be restored with an undo (see https://github.com/nothings/stb/issues/734 for details)\n   return 0;\n}\n\n#ifndef STB_TEXTEDIT_KEYTYPE\n#define STB_TEXTEDIT_KEYTYPE int\n#endif\n\n// API key: process a keyboard input\nstatic void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)\n{\nretry:\n   switch (key) {\n      default: {\n         int c = STB_TEXTEDIT_KEYTOTEXT(key);\n         if (c > 0) {\n            STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c;\n\n            // can't add newline in single-line mode\n            if (c == '\\n' && state->single_line)\n               break;\n\n            if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {\n               stb_text_makeundo_replace(str, state, state->cursor, 1, 1);\n               STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);\n               if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {\n                  ++state->cursor;\n                  state->has_preferred_x = 0;\n               }\n            } else {\n               stb_textedit_delete_selection(str,state); // implicitly clamps\n               if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {\n                  stb_text_makeundo_insert(state, state->cursor, 1);\n                  ++state->cursor;\n                  state->has_preferred_x = 0;\n               }\n            }\n         }\n         break;\n      }\n\n#ifdef STB_TEXTEDIT_K_INSERT\n      case STB_TEXTEDIT_K_INSERT:\n         state->insert_mode = !state->insert_mode;\n         break;\n#endif\n\n      case STB_TEXTEDIT_K_UNDO:\n         stb_text_undo(str, state);\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_REDO:\n         stb_text_redo(str, state);\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_LEFT:\n         // if currently there's a selection, move cursor to start of selection\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_first(state);\n         else\n            if (state->cursor > 0)\n               --state->cursor;\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_RIGHT:\n         // if currently there's a selection, move cursor to end of selection\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_last(str, state);\n         else\n            ++state->cursor;\n         stb_textedit_clamp(str, state);\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT:\n         stb_textedit_clamp(str, state);\n         stb_textedit_prep_selection_at_cursor(state);\n         // move selection left\n         if (state->select_end > 0)\n            --state->select_end;\n         state->cursor = state->select_end;\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_MOVEWORDLEFT\n      case STB_TEXTEDIT_K_WORDLEFT:\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_first(state);\n         else {\n            state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);\n            stb_textedit_clamp( str, state );\n         }\n         break;\n\n      case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT:\n         if( !STB_TEXT_HAS_SELECTION( state ) )\n            stb_textedit_prep_selection_at_cursor(state);\n\n         state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor);\n         state->select_end = state->cursor;\n\n         stb_textedit_clamp( str, state );\n         break;\n#endif\n\n#ifdef STB_TEXTEDIT_MOVEWORDRIGHT\n      case STB_TEXTEDIT_K_WORDRIGHT:\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_last(str, state);\n         else {\n            state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);\n            stb_textedit_clamp( str, state );\n         }\n         break;\n\n      case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT:\n         if( !STB_TEXT_HAS_SELECTION( state ) )\n            stb_textedit_prep_selection_at_cursor(state);\n\n         state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor);\n         state->select_end = state->cursor;\n\n         stb_textedit_clamp( str, state );\n         break;\n#endif\n\n      case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:\n         stb_textedit_prep_selection_at_cursor(state);\n         // move selection right\n         ++state->select_end;\n         stb_textedit_clamp(str, state);\n         state->cursor = state->select_end;\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_DOWN:\n      case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT:\n      case STB_TEXTEDIT_K_PGDOWN:\n      case STB_TEXTEDIT_K_PGDOWN | STB_TEXTEDIT_K_SHIFT: {\n         StbFindState find;\n         StbTexteditRow row;\n         int i, j, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;\n         int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGDOWN;\n         int row_count = is_page ? state->row_count_per_page : 1;\n\n         if (!is_page && state->single_line) {\n            // on windows, up&down in single-line behave like left&right\n            key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT);\n            goto retry;\n         }\n\n         if (sel)\n            stb_textedit_prep_selection_at_cursor(state);\n         else if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_last(str, state);\n\n         // compute current position of cursor point\n         stb_textedit_clamp(str, state);\n         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);\n\n         for (j = 0; j < row_count; ++j) {\n            float x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;\n            int start = find.first_char + find.length;\n\n            if (find.length == 0)\n               break;\n\n            // [DEAR IMGUI]\n            // going down while being on the last line shouldn't bring us to that line end\n            if (STB_TEXTEDIT_GETCHAR(str, find.first_char + find.length - 1) != STB_TEXTEDIT_NEWLINE)\n               break;\n\n            // now find character position down a row\n            state->cursor = start;\n            STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);\n            x = row.x0;\n            for (i=0; i < row.num_chars; ++i) {\n               float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);\n               #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE\n               if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)\n                  break;\n               #endif\n               x += dx;\n               if (x > goal_x)\n                  break;\n               ++state->cursor;\n            }\n            stb_textedit_clamp(str, state);\n\n            state->has_preferred_x = 1;\n            state->preferred_x = goal_x;\n\n            if (sel)\n               state->select_end = state->cursor;\n\n            // go to next line\n            find.first_char = find.first_char + find.length;\n            find.length = row.num_chars;\n         }\n         break;\n      }\n\n      case STB_TEXTEDIT_K_UP:\n      case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT:\n      case STB_TEXTEDIT_K_PGUP:\n      case STB_TEXTEDIT_K_PGUP | STB_TEXTEDIT_K_SHIFT: {\n         StbFindState find;\n         StbTexteditRow row;\n         int i, j, prev_scan, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0;\n         int is_page = (key & ~STB_TEXTEDIT_K_SHIFT) == STB_TEXTEDIT_K_PGUP;\n         int row_count = is_page ? state->row_count_per_page : 1;\n\n         if (!is_page && state->single_line) {\n            // on windows, up&down become left&right\n            key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT);\n            goto retry;\n         }\n\n         if (sel)\n            stb_textedit_prep_selection_at_cursor(state);\n         else if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_move_to_first(state);\n\n         // compute current position of cursor point\n         stb_textedit_clamp(str, state);\n         stb_textedit_find_charpos(&find, str, state->cursor, state->single_line);\n\n         for (j = 0; j < row_count; ++j) {\n            float  x, goal_x = state->has_preferred_x ? state->preferred_x : find.x;\n\n            // can only go up if there's a previous row\n            if (find.prev_first == find.first_char)\n               break;\n\n            // now find character position up a row\n            state->cursor = find.prev_first;\n            STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);\n            x = row.x0;\n            for (i=0; i < row.num_chars; ++i) {\n               float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);\n               #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE\n               if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE)\n                  break;\n               #endif\n               x += dx;\n               if (x > goal_x)\n                  break;\n               ++state->cursor;\n            }\n            stb_textedit_clamp(str, state);\n\n            state->has_preferred_x = 1;\n            state->preferred_x = goal_x;\n\n            if (sel)\n               state->select_end = state->cursor;\n\n            // go to previous line\n            // (we need to scan previous line the hard way. maybe we could expose this as a new API function?)\n            prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;\n            while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)\n               --prev_scan;\n            find.first_char = find.prev_first;\n            find.prev_first = prev_scan;\n         }\n         break;\n      }\n\n      case STB_TEXTEDIT_K_DELETE:\n      case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT:\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_delete_selection(str, state);\n         else {\n            int n = STB_TEXTEDIT_STRINGLEN(str);\n            if (state->cursor < n)\n               stb_textedit_delete(str, state, state->cursor, 1);\n         }\n         state->has_preferred_x = 0;\n         break;\n\n      case STB_TEXTEDIT_K_BACKSPACE:\n      case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT:\n         if (STB_TEXT_HAS_SELECTION(state))\n            stb_textedit_delete_selection(str, state);\n         else {\n            stb_textedit_clamp(str, state);\n            if (state->cursor > 0) {\n               stb_textedit_delete(str, state, state->cursor-1, 1);\n               --state->cursor;\n            }\n         }\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_TEXTSTART2\n      case STB_TEXTEDIT_K_TEXTSTART2:\n#endif\n      case STB_TEXTEDIT_K_TEXTSTART:\n         state->cursor = state->select_start = state->select_end = 0;\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_TEXTEND2\n      case STB_TEXTEDIT_K_TEXTEND2:\n#endif\n      case STB_TEXTEDIT_K_TEXTEND:\n         state->cursor = STB_TEXTEDIT_STRINGLEN(str);\n         state->select_start = state->select_end = 0;\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_TEXTSTART2\n      case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT:\n#endif\n      case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT:\n         stb_textedit_prep_selection_at_cursor(state);\n         state->cursor = state->select_end = 0;\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_TEXTEND2\n      case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT:\n#endif\n      case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT:\n         stb_textedit_prep_selection_at_cursor(state);\n         state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str);\n         state->has_preferred_x = 0;\n         break;\n\n\n#ifdef STB_TEXTEDIT_K_LINESTART2\n      case STB_TEXTEDIT_K_LINESTART2:\n#endif\n      case STB_TEXTEDIT_K_LINESTART:\n         stb_textedit_clamp(str, state);\n         stb_textedit_move_to_first(state);\n         if (state->single_line)\n            state->cursor = 0;\n         else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)\n            --state->cursor;\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_LINEEND2\n      case STB_TEXTEDIT_K_LINEEND2:\n#endif\n      case STB_TEXTEDIT_K_LINEEND: {\n         int n = STB_TEXTEDIT_STRINGLEN(str);\n         stb_textedit_clamp(str, state);\n         stb_textedit_move_to_first(state);\n         if (state->single_line)\n             state->cursor = n;\n         else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)\n             ++state->cursor;\n         state->has_preferred_x = 0;\n         break;\n      }\n\n#ifdef STB_TEXTEDIT_K_LINESTART2\n      case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT:\n#endif\n      case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT:\n         stb_textedit_clamp(str, state);\n         stb_textedit_prep_selection_at_cursor(state);\n         if (state->single_line)\n            state->cursor = 0;\n         else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)\n            --state->cursor;\n         state->select_end = state->cursor;\n         state->has_preferred_x = 0;\n         break;\n\n#ifdef STB_TEXTEDIT_K_LINEEND2\n      case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT:\n#endif\n      case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: {\n         int n = STB_TEXTEDIT_STRINGLEN(str);\n         stb_textedit_clamp(str, state);\n         stb_textedit_prep_selection_at_cursor(state);\n         if (state->single_line)\n             state->cursor = n;\n         else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)\n            ++state->cursor;\n         state->select_end = state->cursor;\n         state->has_preferred_x = 0;\n         break;\n      }\n   }\n}\n\n/////////////////////////////////////////////////////////////////////////////\n//\n//      Undo processing\n//\n// @OPTIMIZE: the undo/redo buffer should be circular\n\nstatic void stb_textedit_flush_redo(StbUndoState *state)\n{\n   state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;\n   state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;\n}\n\n// discard the oldest entry in the undo list\nstatic void stb_textedit_discard_undo(StbUndoState *state)\n{\n   if (state->undo_point > 0) {\n      // if the 0th undo state has characters, clean those up\n      if (state->undo_rec[0].char_storage >= 0) {\n         int n = state->undo_rec[0].insert_length, i;\n         // delete n characters from all other records\n         state->undo_char_point -= n;\n         STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE)));\n         for (i=0; i < state->undo_point; ++i)\n            if (state->undo_rec[i].char_storage >= 0)\n               state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it\n      }\n      --state->undo_point;\n      STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0])));\n   }\n}\n\n// discard the oldest entry in the redo list--it's bad if this\n// ever happens, but because undo & redo have to store the actual\n// characters in different cases, the redo character buffer can\n// fill up even though the undo buffer didn't\nstatic void stb_textedit_discard_redo(StbUndoState *state)\n{\n   int k = STB_TEXTEDIT_UNDOSTATECOUNT-1;\n\n   if (state->redo_point <= k) {\n      // if the k'th undo state has characters, clean those up\n      if (state->undo_rec[k].char_storage >= 0) {\n         int n = state->undo_rec[k].insert_length, i;\n         // move the remaining redo character data to the end of the buffer\n         state->redo_char_point += n;\n         STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE)));\n         // adjust the position of all the other records to account for above memmove\n         for (i=state->redo_point; i < k; ++i)\n            if (state->undo_rec[i].char_storage >= 0)\n               state->undo_rec[i].char_storage += n;\n      }\n      // now move all the redo records towards the end of the buffer; the first one is at 'redo_point'\n      // [DEAR IMGUI]\n      size_t move_size = (size_t)((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point - 1) * sizeof(state->undo_rec[0]));\n      const char* buf_begin = (char*)state->undo_rec; (void)buf_begin;\n      const char* buf_end   = (char*)state->undo_rec + sizeof(state->undo_rec); (void)buf_end;\n      IM_ASSERT(((char*)(state->undo_rec + state->redo_point)) >= buf_begin);\n      IM_ASSERT(((char*)(state->undo_rec + state->redo_point + 1) + move_size) <= buf_end);\n      STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, move_size);\n\n      // now move redo_point to point to the new one\n      ++state->redo_point;\n   }\n}\n\nstatic StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars)\n{\n   // any time we create a new undo record, we discard redo\n   stb_textedit_flush_redo(state);\n\n   // if we have no free records, we have to make room, by sliding the\n   // existing records down\n   if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT)\n      stb_textedit_discard_undo(state);\n\n   // if the characters to store won't possibly fit in the buffer, we can't undo\n   if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) {\n      state->undo_point = 0;\n      state->undo_char_point = 0;\n      return NULL;\n   }\n\n   // if we don't have enough free characters in the buffer, we have to make room\n   while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT)\n      stb_textedit_discard_undo(state);\n\n   return &state->undo_rec[state->undo_point++];\n}\n\nstatic STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len)\n{\n   StbUndoRecord *r = stb_text_create_undo_record(state, insert_len);\n   if (r == NULL)\n      return NULL;\n\n   r->where = pos;\n   r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len;\n   r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len;\n\n   if (insert_len == 0) {\n      r->char_storage = -1;\n      return NULL;\n   } else {\n      r->char_storage = state->undo_char_point;\n      state->undo_char_point += insert_len;\n      return &state->undo_char[r->char_storage];\n   }\n}\n\nstatic void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   StbUndoState *s = &state->undostate;\n   StbUndoRecord u, *r;\n   if (s->undo_point == 0)\n      return;\n\n   // we need to do two things: apply the undo record, and create a redo record\n   u = s->undo_rec[s->undo_point-1];\n   r = &s->undo_rec[s->redo_point-1];\n   r->char_storage = -1;\n\n   r->insert_length = u.delete_length;\n   r->delete_length = u.insert_length;\n   r->where = u.where;\n\n   if (u.delete_length) {\n      // if the undo record says to delete characters, then the redo record will\n      // need to re-insert the characters that get deleted, so we need to store\n      // them.\n\n      // there are three cases:\n      //    there's enough room to store the characters\n      //    characters stored for *redoing* don't leave room for redo\n      //    characters stored for *undoing* don't leave room for redo\n      // if the last is true, we have to bail\n\n      if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) {\n         // the undo records take up too much character space; there's no space to store the redo characters\n         r->insert_length = 0;\n      } else {\n         int i;\n\n         // there's definitely room to store the characters eventually\n         while (s->undo_char_point + u.delete_length > s->redo_char_point) {\n            // should never happen:\n            if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)\n               return;\n            // there's currently not enough room, so discard a redo record\n            stb_textedit_discard_redo(s);\n         }\n         r = &s->undo_rec[s->redo_point-1];\n\n         r->char_storage = s->redo_char_point - u.delete_length;\n         s->redo_char_point = s->redo_char_point - u.delete_length;\n\n         // now save the characters\n         for (i=0; i < u.delete_length; ++i)\n            s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i);\n      }\n\n      // now we can carry out the deletion\n      STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length);\n   }\n\n   // check type of recorded action:\n   if (u.insert_length) {\n      // easy case: was a deletion, so we need to insert n characters\n      STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length);\n      s->undo_char_point -= u.insert_length;\n   }\n\n   state->cursor = u.where + u.insert_length;\n\n   s->undo_point--;\n   s->redo_point--;\n}\n\nstatic void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)\n{\n   StbUndoState *s = &state->undostate;\n   StbUndoRecord *u, r;\n   if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT)\n      return;\n\n   // we need to do two things: apply the redo record, and create an undo record\n   u = &s->undo_rec[s->undo_point];\n   r = s->undo_rec[s->redo_point];\n\n   // we KNOW there must be room for the undo record, because the redo record\n   // was derived from an undo record\n\n   u->delete_length = r.insert_length;\n   u->insert_length = r.delete_length;\n   u->where = r.where;\n   u->char_storage = -1;\n\n   if (r.delete_length) {\n      // the redo record requires us to delete characters, so the undo record\n      // needs to store the characters\n\n      if (s->undo_char_point + u->insert_length > s->redo_char_point) {\n         u->insert_length = 0;\n         u->delete_length = 0;\n      } else {\n         int i;\n         u->char_storage = s->undo_char_point;\n         s->undo_char_point = s->undo_char_point + u->insert_length;\n\n         // now save the characters\n         for (i=0; i < u->insert_length; ++i)\n            s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i);\n      }\n\n      STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length);\n   }\n\n   if (r.insert_length) {\n      // easy case: need to insert n characters\n      STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length);\n      s->redo_char_point += r.insert_length;\n   }\n\n   state->cursor = r.where + r.insert_length;\n\n   s->undo_point++;\n   s->redo_point++;\n}\n\nstatic void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length)\n{\n   stb_text_createundo(&state->undostate, where, 0, length);\n}\n\nstatic void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length)\n{\n   int i;\n   STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0);\n   if (p) {\n      for (i=0; i < length; ++i)\n         p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);\n   }\n}\n\nstatic void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length)\n{\n   int i;\n   STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length);\n   if (p) {\n      for (i=0; i < old_length; ++i)\n         p[i] = STB_TEXTEDIT_GETCHAR(str, where+i);\n   }\n}\n\n// reset the state to default\nstatic void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line)\n{\n   state->undostate.undo_point = 0;\n   state->undostate.undo_char_point = 0;\n   state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT;\n   state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT;\n   state->select_end = state->select_start = 0;\n   state->cursor = 0;\n   state->has_preferred_x = 0;\n   state->preferred_x = 0;\n   state->cursor_at_end_of_line = 0;\n   state->initialized = 1;\n   state->single_line = (unsigned char) is_single_line;\n   state->insert_mode = 0;\n   state->row_count_per_page = 0;\n}\n\n// API initialize\nstatic void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line)\n{\n   stb_textedit_clear_state(state, is_single_line);\n}\n\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wcast-qual\"\n#endif\n\nstatic int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len)\n{\n   return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len);\n}\n\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n\n#endif//STB_TEXTEDIT_IMPLEMENTATION\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\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------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "third-party/imgui/imstb_truetype.h",
    "content": "// [DEAR IMGUI]\n// This is a slightly modified version of stb_truetype.h 1.26.\n// Mostly fixing for compiler and static analyzer warnings.\n// Grep for [DEAR IMGUI] to find the changes.\n\n// stb_truetype.h - v1.26 - public domain\n// authored from 2009-2021 by Sean Barrett / RAD Game Tools\n//\n// =======================================================================\n//\n//    NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES\n//\n// This library does no range checking of the offsets found in the file,\n// meaning an attacker can use it to read arbitrary memory.\n//\n// =======================================================================\n//\n//   This library processes TrueType files:\n//        parse files\n//        extract glyph metrics\n//        extract glyph shapes\n//        render glyphs to one-channel bitmaps with antialiasing (box filter)\n//        render glyphs to one-channel SDF bitmaps (signed-distance field/function)\n//\n//   Todo:\n//        non-MS cmaps\n//        crashproof on bad data\n//        hinting? (no longer patented)\n//        cleartype-style AA?\n//        optimize: use simple memory allocator for intermediates\n//        optimize: build edge-list directly from curves\n//        optimize: rasterize directly from curves?\n//\n// ADDITIONAL CONTRIBUTORS\n//\n//   Mikko Mononen: compound shape support, more cmap formats\n//   Tor Andersson: kerning, subpixel rendering\n//   Dougall Johnson: OpenType / Type 2 font handling\n//   Daniel Ribeiro Maciel: basic GPOS-based kerning\n//\n//   Misc other:\n//       Ryan Gordon\n//       Simon Glass\n//       github:IntellectualKitty\n//       Imanol Celaya\n//       Daniel Ribeiro Maciel\n//\n//   Bug/warning reports/fixes:\n//       \"Zer\" on mollyrocket       Fabian \"ryg\" Giesen   github:NiLuJe\n//       Cass Everitt               Martins Mozeiko       github:aloucks\n//       stoiko (Haemimont Games)   Cap Petschulat        github:oyvindjam\n//       Brian Hook                 Omar Cornut           github:vassvik\n//       Walter van Niftrik         Ryan Griege\n//       David Gow                  Peter LaValle\n//       David Given                Sergey Popov\n//       Ivan-Assen Ivanov          Giumo X. Clanjor\n//       Anthony Pesch              Higor Euripedes\n//       Johan Duparc               Thomas Fields\n//       Hou Qiming                 Derek Vinyard\n//       Rob Loach                  Cort Stratton\n//       Kenney Phillis Jr.         Brian Costabile\n//       Ken Voskuil (kaesve)\n//\n// VERSION HISTORY\n//\n//   1.26 (2021-08-28) fix broken rasterizer\n//   1.25 (2021-07-11) many fixes\n//   1.24 (2020-02-05) fix warning\n//   1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)\n//   1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined\n//   1.21 (2019-02-25) fix warning\n//   1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()\n//   1.19 (2018-02-11) GPOS kerning, STBTT_fmod\n//   1.18 (2018-01-29) add missing function\n//   1.17 (2017-07-23) make more arguments const; doc fix\n//   1.16 (2017-07-12) SDF support\n//   1.15 (2017-03-03) make more arguments const\n//   1.14 (2017-01-16) num-fonts-in-TTC function\n//   1.13 (2017-01-02) support OpenType fonts, certain Apple fonts\n//   1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual\n//   1.11 (2016-04-02) fix unused-variable warning\n//   1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef\n//   1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly\n//   1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges\n//   1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;\n//                     variant PackFontRanges to pack and render in separate phases;\n//                     fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);\n//                     fixed an assert() bug in the new rasterizer\n//                     replace assert() with STBTT_assert() in new rasterizer\n//\n//   Full history can be found at the end of this file.\n//\n// LICENSE\n//\n//   See end of file for license information.\n//\n// USAGE\n//\n//   Include this file in whatever places need to refer to it. In ONE C/C++\n//   file, write:\n//      #define STB_TRUETYPE_IMPLEMENTATION\n//   before the #include of this file. This expands out the actual\n//   implementation into that C/C++ file.\n//\n//   To make the implementation private to the file that generates the implementation,\n//      #define STBTT_STATIC\n//\n//   Simple 3D API (don't ship this, but it's fine for tools and quick start)\n//           stbtt_BakeFontBitmap()               -- bake a font to a bitmap for use as texture\n//           stbtt_GetBakedQuad()                 -- compute quad to draw for a given char\n//\n//   Improved 3D API (more shippable):\n//           #include \"stb_rect_pack.h\"           -- optional, but you really want it\n//           stbtt_PackBegin()\n//           stbtt_PackSetOversampling()          -- for improved quality on small fonts\n//           stbtt_PackFontRanges()               -- pack and renders\n//           stbtt_PackEnd()\n//           stbtt_GetPackedQuad()\n//\n//   \"Load\" a font file from a memory buffer (you have to keep the buffer loaded)\n//           stbtt_InitFont()\n//           stbtt_GetFontOffsetForIndex()        -- indexing for TTC font collections\n//           stbtt_GetNumberOfFonts()             -- number of fonts for TTC font collections\n//\n//   Render a unicode codepoint to a bitmap\n//           stbtt_GetCodepointBitmap()           -- allocates and returns a bitmap\n//           stbtt_MakeCodepointBitmap()          -- renders into bitmap you provide\n//           stbtt_GetCodepointBitmapBox()        -- how big the bitmap must be\n//\n//   Character advance/positioning\n//           stbtt_GetCodepointHMetrics()\n//           stbtt_GetFontVMetrics()\n//           stbtt_GetFontVMetricsOS2()\n//           stbtt_GetCodepointKernAdvance()\n//\n//   Starting with version 1.06, the rasterizer was replaced with a new,\n//   faster and generally-more-precise rasterizer. The new rasterizer more\n//   accurately measures pixel coverage for anti-aliasing, except in the case\n//   where multiple shapes overlap, in which case it overestimates the AA pixel\n//   coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If\n//   this turns out to be a problem, you can re-enable the old rasterizer with\n//        #define STBTT_RASTERIZER_VERSION 1\n//   which will incur about a 15% speed hit.\n//\n// ADDITIONAL DOCUMENTATION\n//\n//   Immediately after this block comment are a series of sample programs.\n//\n//   After the sample programs is the \"header file\" section. This section\n//   includes documentation for each API function.\n//\n//   Some important concepts to understand to use this library:\n//\n//      Codepoint\n//         Characters are defined by unicode codepoints, e.g. 65 is\n//         uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is\n//         the hiragana for \"ma\".\n//\n//      Glyph\n//         A visual character shape (every codepoint is rendered as\n//         some glyph)\n//\n//      Glyph index\n//         A font-specific integer ID representing a glyph\n//\n//      Baseline\n//         Glyph shapes are defined relative to a baseline, which is the\n//         bottom of uppercase characters. Characters extend both above\n//         and below the baseline.\n//\n//      Current Point\n//         As you draw text to the screen, you keep track of a \"current point\"\n//         which is the origin of each character. The current point's vertical\n//         position is the baseline. Even \"baked fonts\" use this model.\n//\n//      Vertical Font Metrics\n//         The vertical qualities of the font, used to vertically position\n//         and space the characters. See docs for stbtt_GetFontVMetrics.\n//\n//      Font Size in Pixels or Points\n//         The preferred interface for specifying font sizes in stb_truetype\n//         is to specify how tall the font's vertical extent should be in pixels.\n//         If that sounds good enough, skip the next paragraph.\n//\n//         Most font APIs instead use \"points\", which are a common typographic\n//         measurement for describing font size, defined as 72 points per inch.\n//         stb_truetype provides a point API for compatibility. However, true\n//         \"per inch\" conventions don't make much sense on computer displays\n//         since different monitors have different number of pixels per\n//         inch. For example, Windows traditionally uses a convention that\n//         there are 96 pixels per inch, thus making 'inch' measurements have\n//         nothing to do with inches, and thus effectively defining a point to\n//         be 1.333 pixels. Additionally, the TrueType font data provides\n//         an explicit scale factor to scale a given font's glyphs to points,\n//         but the author has observed that this scale factor is often wrong\n//         for non-commercial fonts, thus making fonts scaled in points\n//         according to the TrueType spec incoherently sized in practice.\n//\n// DETAILED USAGE:\n//\n//  Scale:\n//    Select how high you want the font to be, in points or pixels.\n//    Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute\n//    a scale factor SF that will be used by all other functions.\n//\n//  Baseline:\n//    You need to select a y-coordinate that is the baseline of where\n//    your text will appear. Call GetFontBoundingBox to get the baseline-relative\n//    bounding box for all characters. SF*-y0 will be the distance in pixels\n//    that the worst-case character could extend above the baseline, so if\n//    you want the top edge of characters to appear at the top of the\n//    screen where y=0, then you would set the baseline to SF*-y0.\n//\n//  Current point:\n//    Set the current point where the first character will appear. The\n//    first character could extend left of the current point; this is font\n//    dependent. You can either choose a current point that is the leftmost\n//    point and hope, or add some padding, or check the bounding box or\n//    left-side-bearing of the first character to be displayed and set\n//    the current point based on that.\n//\n//  Displaying a character:\n//    Compute the bounding box of the character. It will contain signed values\n//    relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,\n//    then the character should be displayed in the rectangle from\n//    <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).\n//\n//  Advancing for the next character:\n//    Call GlyphHMetrics, and compute 'current_point += SF * advance'.\n//\n//\n// ADVANCED USAGE\n//\n//   Quality:\n//\n//    - Use the functions with Subpixel at the end to allow your characters\n//      to have subpixel positioning. Since the font is anti-aliased, not\n//      hinted, this is very import for quality. (This is not possible with\n//      baked fonts.)\n//\n//    - Kerning is now supported, and if you're supporting subpixel rendering\n//      then kerning is worth using to give your text a polished look.\n//\n//   Performance:\n//\n//    - Convert Unicode codepoints to glyph indexes and operate on the glyphs;\n//      if you don't do this, stb_truetype is forced to do the conversion on\n//      every call.\n//\n//    - There are a lot of memory allocations. We should modify it to take\n//      a temp buffer and allocate from the temp buffer (without freeing),\n//      should help performance a lot.\n//\n// NOTES\n//\n//   The system uses the raw data found in the .ttf file without changing it\n//   and without building auxiliary data structures. This is a bit inefficient\n//   on little-endian systems (the data is big-endian), but assuming you're\n//   caching the bitmaps or glyph shapes this shouldn't be a big deal.\n//\n//   It appears to be very hard to programmatically determine what font a\n//   given file is in a general way. I provide an API for this, but I don't\n//   recommend it.\n//\n//\n// PERFORMANCE MEASUREMENTS FOR 1.06:\n//\n//                      32-bit     64-bit\n//   Previous release:  8.83 s     7.68 s\n//   Pool allocations:  7.72 s     6.34 s\n//   Inline sort     :  6.54 s     5.65 s\n//   New rasterizer  :  5.63 s     5.00 s\n\n//////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////\n////\n////  SAMPLE PROGRAMS\n////\n//\n//  Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.\n//  See \"tests/truetype_demo_win32.c\" for a complete version.\n#if 0\n#define STB_TRUETYPE_IMPLEMENTATION  // force following include to generate implementation\n#include \"stb_truetype.h\"\n\nunsigned char ttf_buffer[1<<20];\nunsigned char temp_bitmap[512*512];\n\nstbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs\nGLuint ftex;\n\nvoid my_stbtt_initfont(void)\n{\n   fread(ttf_buffer, 1, 1<<20, fopen(\"c:/windows/fonts/times.ttf\", \"rb\"));\n   stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!\n   // can free ttf_buffer at this point\n   glGenTextures(1, &ftex);\n   glBindTexture(GL_TEXTURE_2D, ftex);\n   glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);\n   // can free temp_bitmap at this point\n   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n}\n\nvoid my_stbtt_print(float x, float y, char *text)\n{\n   // assume orthographic projection with units = screen pixels, origin at top left\n   glEnable(GL_BLEND);\n   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);\n   glEnable(GL_TEXTURE_2D);\n   glBindTexture(GL_TEXTURE_2D, ftex);\n   glBegin(GL_QUADS);\n   while (*text) {\n      if (*text >= 32 && *text < 128) {\n         stbtt_aligned_quad q;\n         stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9\n         glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0);\n         glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);\n         glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);\n         glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);\n      }\n      ++text;\n   }\n   glEnd();\n}\n#endif\n//\n//\n//////////////////////////////////////////////////////////////////////////////\n//\n// Complete program (this compiles): get a single bitmap, print as ASCII art\n//\n#if 0\n#include <stdio.h>\n#define STB_TRUETYPE_IMPLEMENTATION  // force following include to generate implementation\n#include \"stb_truetype.h\"\n\nchar ttf_buffer[1<<25];\n\nint main(int argc, char **argv)\n{\n   stbtt_fontinfo font;\n   unsigned char *bitmap;\n   int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);\n\n   fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : \"c:/windows/fonts/arialbd.ttf\", \"rb\"));\n\n   stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));\n   bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);\n\n   for (j=0; j < h; ++j) {\n      for (i=0; i < w; ++i)\n         putchar(\" .:ioVM@\"[bitmap[j*w+i]>>5]);\n      putchar('\\n');\n   }\n   return 0;\n}\n#endif\n//\n// Output:\n//\n//     .ii.\n//    @@@@@@.\n//   V@Mio@@o\n//   :i.  V@V\n//     :oM@@M\n//   :@@@MM@M\n//   @@o  o@M\n//  :@@.  M@M\n//   @@@o@@@@\n//   :M@@V:@@.\n//\n//////////////////////////////////////////////////////////////////////////////\n//\n// Complete program: print \"Hello World!\" banner, with bugs\n//\n#if 0\nchar buffer[24<<20];\nunsigned char screen[20][79];\n\nint main(int arg, char **argv)\n{\n   stbtt_fontinfo font;\n   int i,j,ascent,baseline,ch=0;\n   float scale, xpos=2; // leave a little padding in case the character extends left\n   char *text = \"Heljo World!\"; // intentionally misspelled to show 'lj' brokenness\n\n   fread(buffer, 1, 1000000, fopen(\"c:/windows/fonts/arialbd.ttf\", \"rb\"));\n   stbtt_InitFont(&font, buffer, 0);\n\n   scale = stbtt_ScaleForPixelHeight(&font, 15);\n   stbtt_GetFontVMetrics(&font, &ascent,0,0);\n   baseline = (int) (ascent*scale);\n\n   while (text[ch]) {\n      int advance,lsb,x0,y0,x1,y1;\n      float x_shift = xpos - (float) floor(xpos);\n      stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);\n      stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);\n      stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);\n      // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong\n      // because this API is really for baking character bitmaps into textures. if you want to render\n      // a sequence of characters, you really need to render each bitmap to a temp buffer, then\n      // \"alpha blend\" that into the working buffer\n      xpos += (advance * scale);\n      if (text[ch+1])\n         xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);\n      ++ch;\n   }\n\n   for (j=0; j < 20; ++j) {\n      for (i=0; i < 78; ++i)\n         putchar(\" .:ioVM@\"[screen[j][i]>>5]);\n      putchar('\\n');\n   }\n\n   return 0;\n}\n#endif\n\n\n//////////////////////////////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////\n////\n////   INTEGRATION WITH YOUR CODEBASE\n////\n////   The following sections allow you to supply alternate definitions\n////   of C library functions used by stb_truetype, e.g. if you don't\n////   link with the C runtime library.\n\n#ifdef STB_TRUETYPE_IMPLEMENTATION\n   // #define your own (u)stbtt_int8/16/32 before including to override this\n   #ifndef stbtt_uint8\n   typedef unsigned char   stbtt_uint8;\n   typedef signed   char   stbtt_int8;\n   typedef unsigned short  stbtt_uint16;\n   typedef signed   short  stbtt_int16;\n   typedef unsigned int    stbtt_uint32;\n   typedef signed   int    stbtt_int32;\n   #endif\n\n   typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];\n   typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];\n\n   // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h\n   #ifndef STBTT_ifloor\n   #include <math.h>\n   #define STBTT_ifloor(x)   ((int) floor(x))\n   #define STBTT_iceil(x)    ((int) ceil(x))\n   #endif\n\n   #ifndef STBTT_sqrt\n   #include <math.h>\n   #define STBTT_sqrt(x)      sqrt(x)\n   #define STBTT_pow(x,y)     pow(x,y)\n   #endif\n\n   #ifndef STBTT_fmod\n   #include <math.h>\n   #define STBTT_fmod(x,y)    fmod(x,y)\n   #endif\n\n   #ifndef STBTT_cos\n   #include <math.h>\n   #define STBTT_cos(x)       cos(x)\n   #define STBTT_acos(x)      acos(x)\n   #endif\n\n   #ifndef STBTT_fabs\n   #include <math.h>\n   #define STBTT_fabs(x)      fabs(x)\n   #endif\n\n   // #define your own functions \"STBTT_malloc\" / \"STBTT_free\" to avoid malloc.h\n   #ifndef STBTT_malloc\n   #include <stdlib.h>\n   #define STBTT_malloc(x,u)  ((void)(u),malloc(x))\n   #define STBTT_free(x,u)    ((void)(u),free(x))\n   #endif\n\n   #ifndef STBTT_assert\n   #include <assert.h>\n   #define STBTT_assert(x)    assert(x)\n   #endif\n\n   #ifndef STBTT_strlen\n   #include <string.h>\n   #define STBTT_strlen(x)    strlen(x)\n   #endif\n\n   #ifndef STBTT_memcpy\n   #include <string.h>\n   #define STBTT_memcpy       memcpy\n   #define STBTT_memset       memset\n   #endif\n#endif\n\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n////\n////   INTERFACE\n////\n////\n\n#ifndef __STB_INCLUDE_STB_TRUETYPE_H__\n#define __STB_INCLUDE_STB_TRUETYPE_H__\n\n#ifdef STBTT_STATIC\n#define STBTT_DEF static\n#else\n#define STBTT_DEF extern\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// private structure\ntypedef struct\n{\n   unsigned char *data;\n   int cursor;\n   int size;\n} stbtt__buf;\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// TEXTURE BAKING API\n//\n// If you use this API, you only have to call two functions ever.\n//\n\ntypedef struct\n{\n   unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap\n   float xoff,yoff,xadvance;\n} stbtt_bakedchar;\n\nSTBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,  // font location (use offset=0 for plain .ttf)\n                                float pixel_height,                     // height of font in pixels\n                                unsigned char *pixels, int pw, int ph,  // bitmap to be filled in\n                                int first_char, int num_chars,          // characters to bake\n                                stbtt_bakedchar *chardata);             // you allocate this, it's num_chars long\n// if return is positive, the first unused row of the bitmap\n// if return is negative, returns the negative of the number of characters that fit\n// if return is 0, no characters fit and no rows were used\n// This uses a very crappy packing.\n\ntypedef struct\n{\n   float x0,y0,s0,t0; // top-left\n   float x1,y1,s1,t1; // bottom-right\n} stbtt_aligned_quad;\n\nSTBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph,  // same data as above\n                               int char_index,             // character to display\n                               float *xpos, float *ypos,   // pointers to current position in screen pixel space\n                               stbtt_aligned_quad *q,      // output: quad to draw\n                               int opengl_fillrule);       // true if opengl fill rule; false if DX9 or earlier\n// Call GetBakedQuad with char_index = 'character - first_char', and it\n// creates the quad you need to draw and advances the current position.\n//\n// The coordinate system used assumes y increases downwards.\n//\n// Characters will extend both above and below the current position;\n// see discussion of \"BASELINE\" above.\n//\n// It's inefficient; you might want to c&p it and optimize it.\n\nSTBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap);\n// Query the font vertical metrics without having to create a font first.\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// NEW TEXTURE BAKING API\n//\n// This provides options for packing multiple fonts into one atlas, not\n// perfectly but better than nothing.\n\ntypedef struct\n{\n   unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap\n   float xoff,yoff,xadvance;\n   float xoff2,yoff2;\n} stbtt_packedchar;\n\ntypedef struct stbtt_pack_context stbtt_pack_context;\ntypedef struct stbtt_fontinfo stbtt_fontinfo;\n#ifndef STB_RECT_PACK_VERSION\ntypedef struct stbrp_rect stbrp_rect;\n#endif\n\nSTBTT_DEF int  stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);\n// Initializes a packing context stored in the passed-in stbtt_pack_context.\n// Future calls using this context will pack characters into the bitmap passed\n// in here: a 1-channel bitmap that is width * height. stride_in_bytes is\n// the distance from one row to the next (or 0 to mean they are packed tightly\n// together). \"padding\" is the amount of padding to leave between each\n// character (normally you want '1' for bitmaps you'll use as textures with\n// bilinear filtering).\n//\n// Returns 0 on failure, 1 on success.\n\nSTBTT_DEF void stbtt_PackEnd  (stbtt_pack_context *spc);\n// Cleans up the packing context and frees all memory.\n\n#define STBTT_POINT_SIZE(x)   (-(x))\n\nSTBTT_DEF int  stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,\n                                int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);\n// Creates character bitmaps from the font_index'th font found in fontdata (use\n// font_index=0 if you don't know what that is). It creates num_chars_in_range\n// bitmaps for characters with unicode values starting at first_unicode_char_in_range\n// and increasing. Data for how to render them is stored in chardata_for_range;\n// pass these to stbtt_GetPackedQuad to get back renderable quads.\n//\n// font_size is the full height of the character from ascender to descender,\n// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed\n// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()\n// and pass that result as 'font_size':\n//       ...,                  20 , ... // font max minus min y is 20 pixels tall\n//       ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall\n\ntypedef struct\n{\n   float font_size;\n   int first_unicode_codepoint_in_range;  // if non-zero, then the chars are continuous, and this is the first codepoint\n   int *array_of_unicode_codepoints;       // if non-zero, then this is an array of unicode codepoints\n   int num_chars;\n   stbtt_packedchar *chardata_for_range; // output\n   unsigned char h_oversample, v_oversample; // don't set these, they're used internally\n} stbtt_pack_range;\n\nSTBTT_DEF int  stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);\n// Creates character bitmaps from multiple ranges of characters stored in\n// ranges. This will usually create a better-packed bitmap than multiple\n// calls to stbtt_PackFontRange. Note that you can call this multiple\n// times within a single PackBegin/PackEnd.\n\nSTBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);\n// Oversampling a font increases the quality by allowing higher-quality subpixel\n// positioning, and is especially valuable at smaller text sizes.\n//\n// This function sets the amount of oversampling for all following calls to\n// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given\n// pack context. The default (no oversampling) is achieved by h_oversample=1\n// and v_oversample=1. The total number of pixels required is\n// h_oversample*v_oversample larger than the default; for example, 2x2\n// oversampling requires 4x the storage of 1x1. For best results, render\n// oversampled textures with bilinear filtering. Look at the readme in\n// stb/tests/oversample for information about oversampled fonts\n//\n// To use with PackFontRangesGather etc., you must set it before calls\n// call to PackFontRangesGatherRects.\n\nSTBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);\n// If skip != 0, this tells stb_truetype to skip any codepoints for which\n// there is no corresponding glyph. If skip=0, which is the default, then\n// codepoints without a glyph recived the font's \"missing character\" glyph,\n// typically an empty box by convention.\n\nSTBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph,  // same data as above\n                               int char_index,             // character to display\n                               float *xpos, float *ypos,   // pointers to current position in screen pixel space\n                               stbtt_aligned_quad *q,      // output: quad to draw\n                               int align_to_integer);\n\nSTBTT_DEF int  stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);\nSTBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);\nSTBTT_DEF int  stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);\n// Calling these functions in sequence is roughly equivalent to calling\n// stbtt_PackFontRanges(). If you more control over the packing of multiple\n// fonts, or if you want to pack custom data into a font texture, take a look\n// at the source to of stbtt_PackFontRanges() and create a custom version\n// using these functions, e.g. call GatherRects multiple times,\n// building up a single array of rects, then call PackRects once,\n// then call RenderIntoRects repeatedly. This may result in a\n// better packing than calling PackFontRanges multiple times\n// (or it may not).\n\n// this is an opaque structure that you shouldn't mess with which holds\n// all the context needed from PackBegin to PackEnd.\nstruct stbtt_pack_context {\n   void *user_allocator_context;\n   void *pack_info;\n   int   width;\n   int   height;\n   int   stride_in_bytes;\n   int   padding;\n   int   skip_missing;\n   unsigned int   h_oversample, v_oversample;\n   unsigned char *pixels;\n   void  *nodes;\n};\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// FONT LOADING\n//\n//\n\nSTBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data);\n// This function will determine the number of fonts in a font file.  TrueType\n// collection (.ttc) files may contain multiple fonts, while TrueType font\n// (.ttf) files only contain one font. The number of fonts can be used for\n// indexing with the previous function where the index is between zero and one\n// less than the total fonts. If an error occurs, -1 is returned.\n\nSTBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);\n// Each .ttf/.ttc file may have more than one font. Each font has a sequential\n// index number starting from 0. Call this function to get the font offset for\n// a given index; it returns -1 if the index is out of range. A regular .ttf\n// file will only define one font and it always be at offset 0, so it will\n// return '0' for index 0, and -1 for all other indices.\n\n// The following structure is defined publicly so you can declare one on\n// the stack or as a global or etc, but you should treat it as opaque.\nstruct stbtt_fontinfo\n{\n   void           * userdata;\n   unsigned char  * data;              // pointer to .ttf file\n   int              fontstart;         // offset of start of font\n\n   int numGlyphs;                     // number of glyphs, needed for range checking\n\n   int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf\n   int index_map;                     // a cmap mapping for our chosen character encoding\n   int indexToLocFormat;              // format needed to map from glyph index to glyph\n\n   stbtt__buf cff;                    // cff font data\n   stbtt__buf charstrings;            // the charstring index\n   stbtt__buf gsubrs;                 // global charstring subroutines index\n   stbtt__buf subrs;                  // private charstring subroutines index\n   stbtt__buf fontdicts;              // array of font dicts\n   stbtt__buf fdselect;               // map from glyph to fontdict\n};\n\nSTBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);\n// Given an offset into the file that defines a font, this function builds\n// the necessary cached info for the rest of the system. You must allocate\n// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't\n// need to do anything special to free it, because the contents are pure\n// value data with no additional data structures. Returns 0 on failure.\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// CHARACTER TO GLYPH-INDEX CONVERSIOn\n\nSTBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);\n// If you're going to perform multiple operations on the same character\n// and you want a speed-up, call this function with the character you're\n// going to process, then use glyph-based functions instead of the\n// codepoint-based functions.\n// Returns 0 if the character codepoint is not defined in the font.\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// CHARACTER PROPERTIES\n//\n\nSTBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);\n// computes a scale factor to produce a font whose \"height\" is 'pixels' tall.\n// Height is measured as the distance from the highest ascender to the lowest\n// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics\n// and computing:\n//       scale = pixels / (ascent - descent)\n// so if you prefer to measure height by the ascent only, use a similar calculation.\n\nSTBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);\n// computes a scale factor to produce a font whose EM size is mapped to\n// 'pixels' tall. This is probably what traditional APIs compute, but\n// I'm not positive.\n\nSTBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);\n// ascent is the coordinate above the baseline the font extends; descent\n// is the coordinate below the baseline the font extends (i.e. it is typically negative)\n// lineGap is the spacing between one row's descent and the next row's ascent...\n// so you should advance the vertical position by \"*ascent - *descent + *lineGap\"\n//   these are expressed in unscaled coordinates, so you must multiply by\n//   the scale factor for a given size\n\nSTBTT_DEF int  stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap);\n// analogous to GetFontVMetrics, but returns the \"typographic\" values from the OS/2\n// table (specific to MS/Windows TTF files).\n//\n// Returns 1 on success (table present), 0 on failure.\n\nSTBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);\n// the bounding box around all possible characters\n\nSTBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);\n// leftSideBearing is the offset from the current horizontal position to the left edge of the character\n// advanceWidth is the offset from the current horizontal position to the next horizontal position\n//   these are expressed in unscaled coordinates\n\nSTBTT_DEF int  stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);\n// an additional amount to add to the 'advance' value between ch1 and ch2\n\nSTBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);\n// Gets the bounding box of the visible part of the glyph, in unscaled coordinates\n\nSTBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);\nSTBTT_DEF int  stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);\nSTBTT_DEF int  stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);\n// as above, but takes one or more glyph indices for greater efficiency\n\ntypedef struct stbtt_kerningentry\n{\n   int glyph1; // use stbtt_FindGlyphIndex\n   int glyph2;\n   int advance;\n} stbtt_kerningentry;\n\nSTBTT_DEF int  stbtt_GetKerningTableLength(const stbtt_fontinfo *info);\nSTBTT_DEF int  stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length);\n// Retrieves a complete list of all of the kerning pairs provided by the font\n// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write.\n// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1)\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// GLYPH SHAPES (you probably don't need these, but they have to go before\n// the bitmaps for C declaration-order reasons)\n//\n\n#ifndef STBTT_vmove // you can predefine these to use different values (but why?)\n   enum {\n      STBTT_vmove=1,\n      STBTT_vline,\n      STBTT_vcurve,\n      STBTT_vcubic\n   };\n#endif\n\n#ifndef stbtt_vertex // you can predefine this to use different values\n                   // (we share this with other code at RAD)\n   #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file\n   typedef struct\n   {\n      stbtt_vertex_type x,y,cx,cy,cx1,cy1;\n      unsigned char type,padding;\n   } stbtt_vertex;\n#endif\n\nSTBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);\n// returns non-zero if nothing is drawn for this glyph\n\nSTBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);\nSTBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);\n// returns # of vertices and fills *vertices with the pointer to them\n//   these are expressed in \"unscaled\" coordinates\n//\n// The shape is a series of contours. Each one starts with\n// a STBTT_moveto, then consists of a series of mixed\n// STBTT_lineto and STBTT_curveto segments. A lineto\n// draws a line from previous endpoint to its x,y; a curveto\n// draws a quadratic bezier from previous endpoint to\n// its x,y, using cx,cy as the bezier control point.\n\nSTBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);\n// frees the data allocated above\n\nSTBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl);\nSTBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg);\nSTBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg);\n// fills svg with the character's SVG data.\n// returns data size or 0 if SVG not found.\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// BITMAP RENDERING\n//\n\nSTBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);\n// frees the bitmap allocated below\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);\n// allocates a large-enough single-channel 8bpp bitmap and renders the\n// specified character/glyph at the specified scale into it, with\n// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).\n// *width & *height are filled out with the width & height of the bitmap,\n// which is stored left-to-right, top-to-bottom.\n//\n// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);\n// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel\n// shift for the character\n\nSTBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);\n// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap\n// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap\n// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the\n// width and height and positioning info for it first.\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);\n// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel\n// shift for the character\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint);\n// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering\n// is performed (see stbtt_PackSetOversampling)\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);\n// get the bbox of the bitmap centered around the glyph origin; so the\n// bitmap width is ix1-ix0, height is iy1-iy0, and location to place\n// the bitmap top left is (leftSideBearing*scale,iy0).\n// (Note that the bitmap uses y-increases-down, but the shape uses\n// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);\n// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel\n// shift for the character\n\n// the following functions are equivalent to the above functions, but operate\n// on glyph indices instead of Unicode codepoints (for efficiency)\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);\nSTBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph);\nSTBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);\nSTBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);\n\n\n// @TODO: don't expose this structure\ntypedef struct\n{\n   int w,h,stride;\n   unsigned char *pixels;\n} stbtt__bitmap;\n\n// rasterize a shape with quadratic beziers into a bitmap\nSTBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result,        // 1-channel bitmap to draw into\n                               float flatness_in_pixels,     // allowable error of curve in pixels\n                               stbtt_vertex *vertices,       // array of vertices defining shape\n                               int num_verts,                // number of vertices in above array\n                               float scale_x, float scale_y, // scale applied to input vertices\n                               float shift_x, float shift_y, // translation applied to input vertices\n                               int x_off, int y_off,         // another translation applied to input\n                               int invert,                   // if non-zero, vertically flip shape\n                               void *userdata);              // context for to STBTT_MALLOC\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Signed Distance Function (or Field) rendering\n\nSTBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata);\n// frees the SDF bitmap allocated below\n\nSTBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);\nSTBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);\n// These functions compute a discretized SDF field for a single character, suitable for storing\n// in a single-channel texture, sampling with bilinear filtering, and testing against\n// larger than some threshold to produce scalable fonts.\n//        info              --  the font\n//        scale             --  controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap\n//        glyph/codepoint   --  the character to generate the SDF for\n//        padding           --  extra \"pixels\" around the character which are filled with the distance to the character (not 0),\n//                                 which allows effects like bit outlines\n//        onedge_value      --  value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character)\n//        pixel_dist_scale  --  what value the SDF should increase by when moving one SDF \"pixel\" away from the edge (on the 0..255 scale)\n//                                 if positive, > onedge_value is inside; if negative, < onedge_value is inside\n//        width,height      --  output height & width of the SDF bitmap (including padding)\n//        xoff,yoff         --  output origin of the character\n//        return value      --  a 2D array of bytes 0..255, width*height in size\n//\n// pixel_dist_scale & onedge_value are a scale & bias that allows you to make\n// optimal use of the limited 0..255 for your application, trading off precision\n// and special effects. SDF values outside the range 0..255 are clamped to 0..255.\n//\n// Example:\n//      scale = stbtt_ScaleForPixelHeight(22)\n//      padding = 5\n//      onedge_value = 180\n//      pixel_dist_scale = 180/5.0 = 36.0\n//\n//      This will create an SDF bitmap in which the character is about 22 pixels\n//      high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled\n//      shape, sample the SDF at each pixel and fill the pixel if the SDF value\n//      is greater than or equal to 180/255. (You'll actually want to antialias,\n//      which is beyond the scope of this example.) Additionally, you can compute\n//      offset outlines (e.g. to stroke the character border inside & outside,\n//      or only outside). For example, to fill outside the character up to 3 SDF\n//      pixels, you would compare against (180-36.0*3)/255 = 72/255. The above\n//      choice of variables maps a range from 5 pixels outside the shape to\n//      2 pixels inside the shape to 0..255; this is intended primarily for apply\n//      outside effects only (the interior range is needed to allow proper\n//      antialiasing of the font at *smaller* sizes)\n//\n// The function computes the SDF analytically at each SDF pixel, not by e.g.\n// building a higher-res bitmap and approximating it. In theory the quality\n// should be as high as possible for an SDF of this size & representation, but\n// unclear if this is true in practice (perhaps building a higher-res bitmap\n// and computing from that can allow drop-out prevention).\n//\n// The algorithm has not been optimized at all, so expect it to be slow\n// if computing lots of characters or very large sizes.\n\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Finding the right font...\n//\n// You should really just solve this offline, keep your own tables\n// of what font is what, and don't try to get it out of the .ttf file.\n// That's because getting it out of the .ttf file is really hard, because\n// the names in the file can appear in many possible encodings, in many\n// possible languages, and e.g. if you need a case-insensitive comparison,\n// the details of that depend on the encoding & language in a complex way\n// (actually underspecified in truetype, but also gigantic).\n//\n// But you can use the provided functions in two possible ways:\n//     stbtt_FindMatchingFont() will use *case-sensitive* comparisons on\n//             unicode-encoded names to try to find the font you want;\n//             you can run this before calling stbtt_InitFont()\n//\n//     stbtt_GetFontNameString() lets you get any of the various strings\n//             from the file yourself and do your own comparisons on them.\n//             You have to have called stbtt_InitFont() first.\n\n\nSTBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);\n// returns the offset (not index) of the font that matches, or -1 if none\n//   if you use STBTT_MACSTYLE_DONTCARE, use a font name like \"Arial Bold\".\n//   if you use any other flag, use a font name like \"Arial\"; this checks\n//     the 'macStyle' header field; i don't know if fonts set this consistently\n#define STBTT_MACSTYLE_DONTCARE     0\n#define STBTT_MACSTYLE_BOLD         1\n#define STBTT_MACSTYLE_ITALIC       2\n#define STBTT_MACSTYLE_UNDERSCORE   4\n#define STBTT_MACSTYLE_NONE         8   // <= not same as 0, this makes us check the bitfield is 0\n\nSTBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);\n// returns 1/0 whether the first string interpreted as utf8 is identical to\n// the second string interpreted as big-endian utf16... useful for strings from next func\n\nSTBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);\n// returns the string (which may be big-endian double byte, e.g. for unicode)\n// and puts the length in bytes in *length.\n//\n// some of the values for the IDs are below; for more see the truetype spec:\n//     http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html\n//     http://www.microsoft.com/typography/otspec/name.htm\n\nenum { // platformID\n   STBTT_PLATFORM_ID_UNICODE   =0,\n   STBTT_PLATFORM_ID_MAC       =1,\n   STBTT_PLATFORM_ID_ISO       =2,\n   STBTT_PLATFORM_ID_MICROSOFT =3\n};\n\nenum { // encodingID for STBTT_PLATFORM_ID_UNICODE\n   STBTT_UNICODE_EID_UNICODE_1_0    =0,\n   STBTT_UNICODE_EID_UNICODE_1_1    =1,\n   STBTT_UNICODE_EID_ISO_10646      =2,\n   STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,\n   STBTT_UNICODE_EID_UNICODE_2_0_FULL=4\n};\n\nenum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT\n   STBTT_MS_EID_SYMBOL        =0,\n   STBTT_MS_EID_UNICODE_BMP   =1,\n   STBTT_MS_EID_SHIFTJIS      =2,\n   STBTT_MS_EID_UNICODE_FULL  =10\n};\n\nenum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes\n   STBTT_MAC_EID_ROMAN        =0,   STBTT_MAC_EID_ARABIC       =4,\n   STBTT_MAC_EID_JAPANESE     =1,   STBTT_MAC_EID_HEBREW       =5,\n   STBTT_MAC_EID_CHINESE_TRAD =2,   STBTT_MAC_EID_GREEK        =6,\n   STBTT_MAC_EID_KOREAN       =3,   STBTT_MAC_EID_RUSSIAN      =7\n};\n\nenum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...\n       // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs\n   STBTT_MS_LANG_ENGLISH     =0x0409,   STBTT_MS_LANG_ITALIAN     =0x0410,\n   STBTT_MS_LANG_CHINESE     =0x0804,   STBTT_MS_LANG_JAPANESE    =0x0411,\n   STBTT_MS_LANG_DUTCH       =0x0413,   STBTT_MS_LANG_KOREAN      =0x0412,\n   STBTT_MS_LANG_FRENCH      =0x040c,   STBTT_MS_LANG_RUSSIAN     =0x0419,\n   STBTT_MS_LANG_GERMAN      =0x0407,   STBTT_MS_LANG_SPANISH     =0x0409,\n   STBTT_MS_LANG_HEBREW      =0x040d,   STBTT_MS_LANG_SWEDISH     =0x041D\n};\n\nenum { // languageID for STBTT_PLATFORM_ID_MAC\n   STBTT_MAC_LANG_ENGLISH      =0 ,   STBTT_MAC_LANG_JAPANESE     =11,\n   STBTT_MAC_LANG_ARABIC       =12,   STBTT_MAC_LANG_KOREAN       =23,\n   STBTT_MAC_LANG_DUTCH        =4 ,   STBTT_MAC_LANG_RUSSIAN      =32,\n   STBTT_MAC_LANG_FRENCH       =1 ,   STBTT_MAC_LANG_SPANISH      =6 ,\n   STBTT_MAC_LANG_GERMAN       =2 ,   STBTT_MAC_LANG_SWEDISH      =5 ,\n   STBTT_MAC_LANG_HEBREW       =10,   STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,\n   STBTT_MAC_LANG_ITALIAN      =3 ,   STBTT_MAC_LANG_CHINESE_TRAD =19\n};\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // __STB_INCLUDE_STB_TRUETYPE_H__\n\n///////////////////////////////////////////////////////////////////////////////\n///////////////////////////////////////////////////////////////////////////////\n////\n////   IMPLEMENTATION\n////\n////\n\n#ifdef STB_TRUETYPE_IMPLEMENTATION\n\n#ifndef STBTT_MAX_OVERSAMPLE\n#define STBTT_MAX_OVERSAMPLE   8\n#endif\n\n#if STBTT_MAX_OVERSAMPLE > 255\n#error \"STBTT_MAX_OVERSAMPLE cannot be > 255\"\n#endif\n\ntypedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];\n\n#ifndef STBTT_RASTERIZER_VERSION\n#define STBTT_RASTERIZER_VERSION 2\n#endif\n\n#ifdef _MSC_VER\n#define STBTT__NOTUSED(v)  (void)(v)\n#else\n#define STBTT__NOTUSED(v)  (void)sizeof(v)\n#endif\n\n//////////////////////////////////////////////////////////////////////////\n//\n// stbtt__buf helpers to parse data from file\n//\n\nstatic stbtt_uint8 stbtt__buf_get8(stbtt__buf *b)\n{\n   if (b->cursor >= b->size)\n      return 0;\n   return b->data[b->cursor++];\n}\n\nstatic stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b)\n{\n   if (b->cursor >= b->size)\n      return 0;\n   return b->data[b->cursor];\n}\n\nstatic void stbtt__buf_seek(stbtt__buf *b, int o)\n{\n   STBTT_assert(!(o > b->size || o < 0));\n   b->cursor = (o > b->size || o < 0) ? b->size : o;\n}\n\nstatic void stbtt__buf_skip(stbtt__buf *b, int o)\n{\n   stbtt__buf_seek(b, b->cursor + o);\n}\n\nstatic stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n)\n{\n   stbtt_uint32 v = 0;\n   int i;\n   STBTT_assert(n >= 1 && n <= 4);\n   for (i = 0; i < n; i++)\n      v = (v << 8) | stbtt__buf_get8(b);\n   return v;\n}\n\nstatic stbtt__buf stbtt__new_buf(const void *p, size_t size)\n{\n   stbtt__buf r;\n   STBTT_assert(size < 0x40000000);\n   r.data = (stbtt_uint8*) p;\n   r.size = (int) size;\n   r.cursor = 0;\n   return r;\n}\n\n#define stbtt__buf_get16(b)  stbtt__buf_get((b), 2)\n#define stbtt__buf_get32(b)  stbtt__buf_get((b), 4)\n\nstatic stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s)\n{\n   stbtt__buf r = stbtt__new_buf(NULL, 0);\n   if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r;\n   r.data = b->data + o;\n   r.size = s;\n   return r;\n}\n\nstatic stbtt__buf stbtt__cff_get_index(stbtt__buf *b)\n{\n   int count, start, offsize;\n   start = b->cursor;\n   count = stbtt__buf_get16(b);\n   if (count) {\n      offsize = stbtt__buf_get8(b);\n      STBTT_assert(offsize >= 1 && offsize <= 4);\n      stbtt__buf_skip(b, offsize * count);\n      stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1);\n   }\n   return stbtt__buf_range(b, start, b->cursor - start);\n}\n\nstatic stbtt_uint32 stbtt__cff_int(stbtt__buf *b)\n{\n   int b0 = stbtt__buf_get8(b);\n   if (b0 >= 32 && b0 <= 246)       return b0 - 139;\n   else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108;\n   else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108;\n   else if (b0 == 28)               return stbtt__buf_get16(b);\n   else if (b0 == 29)               return stbtt__buf_get32(b);\n   STBTT_assert(0);\n   return 0;\n}\n\nstatic void stbtt__cff_skip_operand(stbtt__buf *b) {\n   int v, b0 = stbtt__buf_peek8(b);\n   STBTT_assert(b0 >= 28);\n   if (b0 == 30) {\n      stbtt__buf_skip(b, 1);\n      while (b->cursor < b->size) {\n         v = stbtt__buf_get8(b);\n         if ((v & 0xF) == 0xF || (v >> 4) == 0xF)\n            break;\n      }\n   } else {\n      stbtt__cff_int(b);\n   }\n}\n\nstatic stbtt__buf stbtt__dict_get(stbtt__buf *b, int key)\n{\n   stbtt__buf_seek(b, 0);\n   while (b->cursor < b->size) {\n      int start = b->cursor, end, op;\n      while (stbtt__buf_peek8(b) >= 28)\n         stbtt__cff_skip_operand(b);\n      end = b->cursor;\n      op = stbtt__buf_get8(b);\n      if (op == 12)  op = stbtt__buf_get8(b) | 0x100;\n      if (op == key) return stbtt__buf_range(b, start, end-start);\n   }\n   return stbtt__buf_range(b, 0, 0);\n}\n\nstatic void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out)\n{\n   int i;\n   stbtt__buf operands = stbtt__dict_get(b, key);\n   for (i = 0; i < outcount && operands.cursor < operands.size; i++)\n      out[i] = stbtt__cff_int(&operands);\n}\n\nstatic int stbtt__cff_index_count(stbtt__buf *b)\n{\n   stbtt__buf_seek(b, 0);\n   return stbtt__buf_get16(b);\n}\n\nstatic stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i)\n{\n   int count, offsize, start, end;\n   stbtt__buf_seek(&b, 0);\n   count = stbtt__buf_get16(&b);\n   offsize = stbtt__buf_get8(&b);\n   STBTT_assert(i >= 0 && i < count);\n   STBTT_assert(offsize >= 1 && offsize <= 4);\n   stbtt__buf_skip(&b, i*offsize);\n   start = stbtt__buf_get(&b, offsize);\n   end = stbtt__buf_get(&b, offsize);\n   return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start);\n}\n\n//////////////////////////////////////////////////////////////////////////\n//\n// accessors to parse data from file\n//\n\n// on platforms that don't allow misaligned reads, if we want to allow\n// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE\n\n#define ttBYTE(p)     (* (stbtt_uint8 *) (p))\n#define ttCHAR(p)     (* (stbtt_int8 *) (p))\n#define ttFixed(p)    ttLONG(p)\n\nstatic stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }\nstatic stbtt_int16 ttSHORT(stbtt_uint8 *p)   { return p[0]*256 + p[1]; }\nstatic stbtt_uint32 ttULONG(stbtt_uint8 *p)  { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }\nstatic stbtt_int32 ttLONG(stbtt_uint8 *p)    { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }\n\n#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))\n#define stbtt_tag(p,str)           stbtt_tag4(p,str[0],str[1],str[2],str[3])\n\nstatic int stbtt__isfont(stbtt_uint8 *font)\n{\n   // check the version number\n   if (stbtt_tag4(font, '1',0,0,0))  return 1; // TrueType 1\n   if (stbtt_tag(font, \"typ1\"))   return 1; // TrueType with type 1 font -- we don't support this!\n   if (stbtt_tag(font, \"OTTO\"))   return 1; // OpenType with CFF\n   if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0\n   if (stbtt_tag(font, \"true\"))   return 1; // Apple specification for TrueType fonts\n   return 0;\n}\n\n// @OPTIMIZE: binary search\nstatic stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)\n{\n   stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);\n   stbtt_uint32 tabledir = fontstart + 12;\n   stbtt_int32 i;\n   for (i=0; i < num_tables; ++i) {\n      stbtt_uint32 loc = tabledir + 16*i;\n      if (stbtt_tag(data+loc+0, tag))\n         return ttULONG(data+loc+8);\n   }\n   return 0;\n}\n\nstatic int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index)\n{\n   // if it's just a font, there's only one valid index\n   if (stbtt__isfont(font_collection))\n      return index == 0 ? 0 : -1;\n\n   // check if it's a TTC\n   if (stbtt_tag(font_collection, \"ttcf\")) {\n      // version 1?\n      if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {\n         stbtt_int32 n = ttLONG(font_collection+8);\n         if (index >= n)\n            return -1;\n         return ttULONG(font_collection+12+index*4);\n      }\n   }\n   return -1;\n}\n\nstatic int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection)\n{\n   // if it's just a font, there's only one valid font\n   if (stbtt__isfont(font_collection))\n      return 1;\n\n   // check if it's a TTC\n   if (stbtt_tag(font_collection, \"ttcf\")) {\n      // version 1?\n      if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {\n         return ttLONG(font_collection+8);\n      }\n   }\n   return 0;\n}\n\nstatic stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict)\n{\n   stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 };\n   stbtt__buf pdict;\n   stbtt__dict_get_ints(&fontdict, 18, 2, private_loc);\n   if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0);\n   pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]);\n   stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff);\n   if (!subrsoff) return stbtt__new_buf(NULL, 0);\n   stbtt__buf_seek(&cff, private_loc[1]+subrsoff);\n   return stbtt__cff_get_index(&cff);\n}\n\n// since most people won't use this, find this table the first time it's needed\nstatic int stbtt__get_svg(stbtt_fontinfo *info)\n{\n   stbtt_uint32 t;\n   if (info->svg < 0) {\n      t = stbtt__find_table(info->data, info->fontstart, \"SVG \");\n      if (t) {\n         stbtt_uint32 offset = ttULONG(info->data + t + 2);\n         info->svg = t + offset;\n      } else {\n         info->svg = 0;\n      }\n   }\n   return info->svg;\n}\n\nstatic int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart)\n{\n   stbtt_uint32 cmap, t;\n   stbtt_int32 i,numTables;\n\n   info->data = data;\n   info->fontstart = fontstart;\n   info->cff = stbtt__new_buf(NULL, 0);\n\n   cmap = stbtt__find_table(data, fontstart, \"cmap\");       // required\n   info->loca = stbtt__find_table(data, fontstart, \"loca\"); // required\n   info->head = stbtt__find_table(data, fontstart, \"head\"); // required\n   info->glyf = stbtt__find_table(data, fontstart, \"glyf\"); // required\n   info->hhea = stbtt__find_table(data, fontstart, \"hhea\"); // required\n   info->hmtx = stbtt__find_table(data, fontstart, \"hmtx\"); // required\n   info->kern = stbtt__find_table(data, fontstart, \"kern\"); // not required\n   info->gpos = stbtt__find_table(data, fontstart, \"GPOS\"); // not required\n\n   if (!cmap || !info->head || !info->hhea || !info->hmtx)\n      return 0;\n   if (info->glyf) {\n      // required for truetype\n      if (!info->loca) return 0;\n   } else {\n      // initialization for CFF / Type2 fonts (OTF)\n      stbtt__buf b, topdict, topdictidx;\n      stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0;\n      stbtt_uint32 cff;\n\n      cff = stbtt__find_table(data, fontstart, \"CFF \");\n      if (!cff) return 0;\n\n      info->fontdicts = stbtt__new_buf(NULL, 0);\n      info->fdselect = stbtt__new_buf(NULL, 0);\n\n      // @TODO this should use size from table (not 512MB)\n      info->cff = stbtt__new_buf(data+cff, 512*1024*1024);\n      b = info->cff;\n\n      // read the header\n      stbtt__buf_skip(&b, 2);\n      stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize\n\n      // @TODO the name INDEX could list multiple fonts,\n      // but we just use the first one.\n      stbtt__cff_get_index(&b);  // name INDEX\n      topdictidx = stbtt__cff_get_index(&b);\n      topdict = stbtt__cff_index_get(topdictidx, 0);\n      stbtt__cff_get_index(&b);  // string INDEX\n      info->gsubrs = stbtt__cff_get_index(&b);\n\n      stbtt__dict_get_ints(&topdict, 17, 1, &charstrings);\n      stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype);\n      stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff);\n      stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff);\n      info->subrs = stbtt__get_subrs(b, topdict);\n\n      // we only support Type 2 charstrings\n      if (cstype != 2) return 0;\n      if (charstrings == 0) return 0;\n\n      if (fdarrayoff) {\n         // looks like a CID font\n         if (!fdselectoff) return 0;\n         stbtt__buf_seek(&b, fdarrayoff);\n         info->fontdicts = stbtt__cff_get_index(&b);\n         info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff);\n      }\n\n      stbtt__buf_seek(&b, charstrings);\n      info->charstrings = stbtt__cff_get_index(&b);\n   }\n\n   t = stbtt__find_table(data, fontstart, \"maxp\");\n   if (t)\n      info->numGlyphs = ttUSHORT(data+t+4);\n   else\n      info->numGlyphs = 0xffff;\n\n   info->svg = -1;\n\n   // find a cmap encoding table we understand *now* to avoid searching\n   // later. (todo: could make this installable)\n   // the same regardless of glyph.\n   numTables = ttUSHORT(data + cmap + 2);\n   info->index_map = 0;\n   for (i=0; i < numTables; ++i) {\n      stbtt_uint32 encoding_record = cmap + 4 + 8 * i;\n      // find an encoding we understand:\n      switch(ttUSHORT(data+encoding_record)) {\n         case STBTT_PLATFORM_ID_MICROSOFT:\n            switch (ttUSHORT(data+encoding_record+2)) {\n               case STBTT_MS_EID_UNICODE_BMP:\n               case STBTT_MS_EID_UNICODE_FULL:\n                  // MS/Unicode\n                  info->index_map = cmap + ttULONG(data+encoding_record+4);\n                  break;\n            }\n            break;\n        case STBTT_PLATFORM_ID_UNICODE:\n            // Mac/iOS has these\n            // all the encodingIDs are unicode, so we don't bother to check it\n            info->index_map = cmap + ttULONG(data+encoding_record+4);\n            break;\n      }\n   }\n   if (info->index_map == 0)\n      return 0;\n\n   info->indexToLocFormat = ttUSHORT(data+info->head + 50);\n   return 1;\n}\n\nSTBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)\n{\n   stbtt_uint8 *data = info->data;\n   stbtt_uint32 index_map = info->index_map;\n\n   stbtt_uint16 format = ttUSHORT(data + index_map + 0);\n   if (format == 0) { // apple byte encoding\n      stbtt_int32 bytes = ttUSHORT(data + index_map + 2);\n      if (unicode_codepoint < bytes-6)\n         return ttBYTE(data + index_map + 6 + unicode_codepoint);\n      return 0;\n   } else if (format == 6) {\n      stbtt_uint32 first = ttUSHORT(data + index_map + 6);\n      stbtt_uint32 count = ttUSHORT(data + index_map + 8);\n      if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)\n         return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);\n      return 0;\n   } else if (format == 2) {\n      STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean\n      return 0;\n   } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges\n      stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;\n      stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;\n      stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);\n      stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;\n\n      // do a binary search of the segments\n      stbtt_uint32 endCount = index_map + 14;\n      stbtt_uint32 search = endCount;\n\n      if (unicode_codepoint > 0xffff)\n         return 0;\n\n      // they lie from endCount .. endCount + segCount\n      // but searchRange is the nearest power of two, so...\n      if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))\n         search += rangeShift*2;\n\n      // now decrement to bias correctly to find smallest\n      search -= 2;\n      while (entrySelector) {\n         stbtt_uint16 end;\n         searchRange >>= 1;\n         end = ttUSHORT(data + search + searchRange*2);\n         if (unicode_codepoint > end)\n            search += searchRange*2;\n         --entrySelector;\n      }\n      search += 2;\n\n      {\n         stbtt_uint16 offset, start, last;\n         stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);\n\n         start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);\n         last = ttUSHORT(data + endCount + 2*item);\n         if (unicode_codepoint < start || unicode_codepoint > last)\n            return 0;\n\n         offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);\n         if (offset == 0)\n            return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));\n\n         return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);\n      }\n   } else if (format == 12 || format == 13) {\n      stbtt_uint32 ngroups = ttULONG(data+index_map+12);\n      stbtt_int32 low,high;\n      low = 0; high = (stbtt_int32)ngroups;\n      // Binary search the right group.\n      while (low < high) {\n         stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high\n         stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);\n         stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);\n         if ((stbtt_uint32) unicode_codepoint < start_char)\n            high = mid;\n         else if ((stbtt_uint32) unicode_codepoint > end_char)\n            low = mid+1;\n         else {\n            stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);\n            if (format == 12)\n               return start_glyph + unicode_codepoint-start_char;\n            else // format == 13\n               return start_glyph;\n         }\n      }\n      return 0; // not found\n   }\n   // @TODO\n   STBTT_assert(0);\n   return 0;\n}\n\nSTBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)\n{\n   return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);\n}\n\nstatic void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)\n{\n   v->type = type;\n   v->x = (stbtt_int16) x;\n   v->y = (stbtt_int16) y;\n   v->cx = (stbtt_int16) cx;\n   v->cy = (stbtt_int16) cy;\n}\n\nstatic int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)\n{\n   int g1,g2;\n\n   STBTT_assert(!info->cff.size);\n\n   if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range\n   if (info->indexToLocFormat >= 2)    return -1; // unknown index->glyph map format\n\n   if (info->indexToLocFormat == 0) {\n      g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;\n      g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;\n   } else {\n      g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);\n      g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);\n   }\n\n   return g1==g2 ? -1 : g1; // if length is 0, return -1\n}\n\nstatic int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);\n\nSTBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)\n{\n   if (info->cff.size) {\n      stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1);\n   } else {\n      int g = stbtt__GetGlyfOffset(info, glyph_index);\n      if (g < 0) return 0;\n\n      if (x0) *x0 = ttSHORT(info->data + g + 2);\n      if (y0) *y0 = ttSHORT(info->data + g + 4);\n      if (x1) *x1 = ttSHORT(info->data + g + 6);\n      if (y1) *y1 = ttSHORT(info->data + g + 8);\n   }\n   return 1;\n}\n\nSTBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)\n{\n   return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);\n}\n\nSTBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)\n{\n   stbtt_int16 numberOfContours;\n   int g;\n   if (info->cff.size)\n      return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0;\n   g = stbtt__GetGlyfOffset(info, glyph_index);\n   if (g < 0) return 1;\n   numberOfContours = ttSHORT(info->data + g);\n   return numberOfContours == 0;\n}\n\nstatic int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,\n    stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)\n{\n   if (start_off) {\n      if (was_off)\n         stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);\n      stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);\n   } else {\n      if (was_off)\n         stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);\n      else\n         stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);\n   }\n   return num_vertices;\n}\n\nstatic int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)\n{\n   stbtt_int16 numberOfContours;\n   stbtt_uint8 *endPtsOfContours;\n   stbtt_uint8 *data = info->data;\n   stbtt_vertex *vertices=0;\n   int num_vertices=0;\n   int g = stbtt__GetGlyfOffset(info, glyph_index);\n\n   *pvertices = NULL;\n\n   if (g < 0) return 0;\n\n   numberOfContours = ttSHORT(data + g);\n\n   if (numberOfContours > 0) {\n      stbtt_uint8 flags=0,flagcount;\n      stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;\n      stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;\n      stbtt_uint8 *points;\n      endPtsOfContours = (data + g + 10);\n      ins = ttUSHORT(data + g + 10 + numberOfContours * 2);\n      points = data + g + 10 + numberOfContours * 2 + 2 + ins;\n\n      n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);\n\n      m = n + 2*numberOfContours;  // a loose bound on how many vertices we might need\n      vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);\n      if (vertices == 0)\n         return 0;\n\n      next_move = 0;\n      flagcount=0;\n\n      // in first pass, we load uninterpreted data into the allocated array\n      // above, shifted to the end of the array so we won't overwrite it when\n      // we create our final data starting from the front\n\n      off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated\n\n      // first load flags\n\n      for (i=0; i < n; ++i) {\n         if (flagcount == 0) {\n            flags = *points++;\n            if (flags & 8)\n               flagcount = *points++;\n         } else\n            --flagcount;\n         vertices[off+i].type = flags;\n      }\n\n      // now load x coordinates\n      x=0;\n      for (i=0; i < n; ++i) {\n         flags = vertices[off+i].type;\n         if (flags & 2) {\n            stbtt_int16 dx = *points++;\n            x += (flags & 16) ? dx : -dx; // ???\n         } else {\n            if (!(flags & 16)) {\n               x = x + (stbtt_int16) (points[0]*256 + points[1]);\n               points += 2;\n            }\n         }\n         vertices[off+i].x = (stbtt_int16) x;\n      }\n\n      // now load y coordinates\n      y=0;\n      for (i=0; i < n; ++i) {\n         flags = vertices[off+i].type;\n         if (flags & 4) {\n            stbtt_int16 dy = *points++;\n            y += (flags & 32) ? dy : -dy; // ???\n         } else {\n            if (!(flags & 32)) {\n               y = y + (stbtt_int16) (points[0]*256 + points[1]);\n               points += 2;\n            }\n         }\n         vertices[off+i].y = (stbtt_int16) y;\n      }\n\n      // now convert them to our format\n      num_vertices=0;\n      sx = sy = cx = cy = scx = scy = 0;\n      for (i=0; i < n; ++i) {\n         flags = vertices[off+i].type;\n         x     = (stbtt_int16) vertices[off+i].x;\n         y     = (stbtt_int16) vertices[off+i].y;\n\n         if (next_move == i) {\n            if (i != 0)\n               num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);\n\n            // now start the new one\n            start_off = !(flags & 1);\n            if (start_off) {\n               // if we start off with an off-curve point, then when we need to find a point on the curve\n               // where we can start, and we need to save some state for when we wraparound.\n               scx = x;\n               scy = y;\n               if (!(vertices[off+i+1].type & 1)) {\n                  // next point is also a curve point, so interpolate an on-point curve\n                  sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;\n                  sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;\n               } else {\n                  // otherwise just use the next point as our start point\n                  sx = (stbtt_int32) vertices[off+i+1].x;\n                  sy = (stbtt_int32) vertices[off+i+1].y;\n                  ++i; // we're using point i+1 as the starting point, so skip it\n               }\n            } else {\n               sx = x;\n               sy = y;\n            }\n            stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);\n            was_off = 0;\n            next_move = 1 + ttUSHORT(endPtsOfContours+j*2);\n            ++j;\n         } else {\n            if (!(flags & 1)) { // if it's a curve\n               if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint\n                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);\n               cx = x;\n               cy = y;\n               was_off = 1;\n            } else {\n               if (was_off)\n                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);\n               else\n                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);\n               was_off = 0;\n            }\n         }\n      }\n      num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);\n   } else if (numberOfContours < 0) {\n      // Compound shapes.\n      int more = 1;\n      stbtt_uint8 *comp = data + g + 10;\n      num_vertices = 0;\n      vertices = 0;\n      while (more) {\n         stbtt_uint16 flags, gidx;\n         int comp_num_verts = 0, i;\n         stbtt_vertex *comp_verts = 0, *tmp = 0;\n         float mtx[6] = {1,0,0,1,0,0}, m, n;\n\n         flags = ttSHORT(comp); comp+=2;\n         gidx = ttSHORT(comp); comp+=2;\n\n         if (flags & 2) { // XY values\n            if (flags & 1) { // shorts\n               mtx[4] = ttSHORT(comp); comp+=2;\n               mtx[5] = ttSHORT(comp); comp+=2;\n            } else {\n               mtx[4] = ttCHAR(comp); comp+=1;\n               mtx[5] = ttCHAR(comp); comp+=1;\n            }\n         }\n         else {\n            // @TODO handle matching point\n            STBTT_assert(0);\n         }\n         if (flags & (1<<3)) { // WE_HAVE_A_SCALE\n            mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[1] = mtx[2] = 0;\n         } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE\n            mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[1] = mtx[2] = 0;\n            mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;\n         } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO\n            mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;\n            mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;\n         }\n\n         // Find transformation scales.\n         m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);\n         n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);\n\n         // Get indexed glyph.\n         comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);\n         if (comp_num_verts > 0) {\n            // Transform vertices.\n            for (i = 0; i < comp_num_verts; ++i) {\n               stbtt_vertex* v = &comp_verts[i];\n               stbtt_vertex_type x,y;\n               x=v->x; y=v->y;\n               v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));\n               v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));\n               x=v->cx; y=v->cy;\n               v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));\n               v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));\n            }\n            // Append vertices.\n            tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);\n            if (!tmp) {\n               if (vertices) STBTT_free(vertices, info->userdata);\n               if (comp_verts) STBTT_free(comp_verts, info->userdata);\n               return 0;\n            }\n            if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));\n            STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));\n            if (vertices) STBTT_free(vertices, info->userdata);\n            vertices = tmp;\n            STBTT_free(comp_verts, info->userdata);\n            num_vertices += comp_num_verts;\n         }\n         // More components ?\n         more = flags & (1<<5);\n      }\n   } else {\n      // numberOfCounters == 0, do nothing\n   }\n\n   *pvertices = vertices;\n   return num_vertices;\n}\n\ntypedef struct\n{\n   int bounds;\n   int started;\n   float first_x, first_y;\n   float x, y;\n   stbtt_int32 min_x, max_x, min_y, max_y;\n\n   stbtt_vertex *pvertices;\n   int num_vertices;\n} stbtt__csctx;\n\n#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0}\n\nstatic void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y)\n{\n   if (x > c->max_x || !c->started) c->max_x = x;\n   if (y > c->max_y || !c->started) c->max_y = y;\n   if (x < c->min_x || !c->started) c->min_x = x;\n   if (y < c->min_y || !c->started) c->min_y = y;\n   c->started = 1;\n}\n\nstatic void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1)\n{\n   if (c->bounds) {\n      stbtt__track_vertex(c, x, y);\n      if (type == STBTT_vcubic) {\n         stbtt__track_vertex(c, cx, cy);\n         stbtt__track_vertex(c, cx1, cy1);\n      }\n   } else {\n      stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy);\n      c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1;\n      c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1;\n   }\n   c->num_vertices++;\n}\n\nstatic void stbtt__csctx_close_shape(stbtt__csctx *ctx)\n{\n   if (ctx->first_x != ctx->x || ctx->first_y != ctx->y)\n      stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0);\n}\n\nstatic void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy)\n{\n   stbtt__csctx_close_shape(ctx);\n   ctx->first_x = ctx->x = ctx->x + dx;\n   ctx->first_y = ctx->y = ctx->y + dy;\n   stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);\n}\n\nstatic void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy)\n{\n   ctx->x += dx;\n   ctx->y += dy;\n   stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);\n}\n\nstatic void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)\n{\n   float cx1 = ctx->x + dx1;\n   float cy1 = ctx->y + dy1;\n   float cx2 = cx1 + dx2;\n   float cy2 = cy1 + dy2;\n   ctx->x = cx2 + dx3;\n   ctx->y = cy2 + dy3;\n   stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2);\n}\n\nstatic stbtt__buf stbtt__get_subr(stbtt__buf idx, int n)\n{\n   int count = stbtt__cff_index_count(&idx);\n   int bias = 107;\n   if (count >= 33900)\n      bias = 32768;\n   else if (count >= 1240)\n      bias = 1131;\n   n += bias;\n   if (n < 0 || n >= count)\n      return stbtt__new_buf(NULL, 0);\n   return stbtt__cff_index_get(idx, n);\n}\n\nstatic stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index)\n{\n   stbtt__buf fdselect = info->fdselect;\n   int nranges, start, end, v, fmt, fdselector = -1, i;\n\n   stbtt__buf_seek(&fdselect, 0);\n   fmt = stbtt__buf_get8(&fdselect);\n   if (fmt == 0) {\n      // untested\n      stbtt__buf_skip(&fdselect, glyph_index);\n      fdselector = stbtt__buf_get8(&fdselect);\n   } else if (fmt == 3) {\n      nranges = stbtt__buf_get16(&fdselect);\n      start = stbtt__buf_get16(&fdselect);\n      for (i = 0; i < nranges; i++) {\n         v = stbtt__buf_get8(&fdselect);\n         end = stbtt__buf_get16(&fdselect);\n         if (glyph_index >= start && glyph_index < end) {\n            fdselector = v;\n            break;\n         }\n         start = end;\n      }\n   }\n   if (fdselector == -1) return stbtt__new_buf(NULL, 0); // [DEAR IMGUI] fixed, see #6007 and nothings/stb#1422\n   return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));\n}\n\nstatic int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c)\n{\n   int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0;\n   int has_subrs = 0, clear_stack;\n   float s[48];\n   stbtt__buf subr_stack[10], subrs = info->subrs, b;\n   float f;\n\n#define STBTT__CSERR(s) (0)\n\n   // this currently ignores the initial width value, which isn't needed if we have hmtx\n   b = stbtt__cff_index_get(info->charstrings, glyph_index);\n   while (b.cursor < b.size) {\n      i = 0;\n      clear_stack = 1;\n      b0 = stbtt__buf_get8(&b);\n      switch (b0) {\n      // @TODO implement hinting\n      case 0x13: // hintmask\n      case 0x14: // cntrmask\n         if (in_header)\n            maskbits += (sp / 2); // implicit \"vstem\"\n         in_header = 0;\n         stbtt__buf_skip(&b, (maskbits + 7) / 8);\n         break;\n\n      case 0x01: // hstem\n      case 0x03: // vstem\n      case 0x12: // hstemhm\n      case 0x17: // vstemhm\n         maskbits += (sp / 2);\n         break;\n\n      case 0x15: // rmoveto\n         in_header = 0;\n         if (sp < 2) return STBTT__CSERR(\"rmoveto stack\");\n         stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]);\n         break;\n      case 0x04: // vmoveto\n         in_header = 0;\n         if (sp < 1) return STBTT__CSERR(\"vmoveto stack\");\n         stbtt__csctx_rmove_to(c, 0, s[sp-1]);\n         break;\n      case 0x16: // hmoveto\n         in_header = 0;\n         if (sp < 1) return STBTT__CSERR(\"hmoveto stack\");\n         stbtt__csctx_rmove_to(c, s[sp-1], 0);\n         break;\n\n      case 0x05: // rlineto\n         if (sp < 2) return STBTT__CSERR(\"rlineto stack\");\n         for (; i + 1 < sp; i += 2)\n            stbtt__csctx_rline_to(c, s[i], s[i+1]);\n         break;\n\n      // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical\n      // starting from a different place.\n\n      case 0x07: // vlineto\n         if (sp < 1) return STBTT__CSERR(\"vlineto stack\");\n         goto vlineto;\n      case 0x06: // hlineto\n         if (sp < 1) return STBTT__CSERR(\"hlineto stack\");\n         for (;;) {\n            if (i >= sp) break;\n            stbtt__csctx_rline_to(c, s[i], 0);\n            i++;\n      vlineto:\n            if (i >= sp) break;\n            stbtt__csctx_rline_to(c, 0, s[i]);\n            i++;\n         }\n         break;\n\n      case 0x1F: // hvcurveto\n         if (sp < 4) return STBTT__CSERR(\"hvcurveto stack\");\n         goto hvcurveto;\n      case 0x1E: // vhcurveto\n         if (sp < 4) return STBTT__CSERR(\"vhcurveto stack\");\n         for (;;) {\n            if (i + 3 >= sp) break;\n            stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f);\n            i += 4;\n      hvcurveto:\n            if (i + 3 >= sp) break;\n            stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]);\n            i += 4;\n         }\n         break;\n\n      case 0x08: // rrcurveto\n         if (sp < 6) return STBTT__CSERR(\"rcurveline stack\");\n         for (; i + 5 < sp; i += 6)\n            stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);\n         break;\n\n      case 0x18: // rcurveline\n         if (sp < 8) return STBTT__CSERR(\"rcurveline stack\");\n         for (; i + 5 < sp - 2; i += 6)\n            stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);\n         if (i + 1 >= sp) return STBTT__CSERR(\"rcurveline stack\");\n         stbtt__csctx_rline_to(c, s[i], s[i+1]);\n         break;\n\n      case 0x19: // rlinecurve\n         if (sp < 8) return STBTT__CSERR(\"rlinecurve stack\");\n         for (; i + 1 < sp - 6; i += 2)\n            stbtt__csctx_rline_to(c, s[i], s[i+1]);\n         if (i + 5 >= sp) return STBTT__CSERR(\"rlinecurve stack\");\n         stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);\n         break;\n\n      case 0x1A: // vvcurveto\n      case 0x1B: // hhcurveto\n         if (sp < 4) return STBTT__CSERR(\"(vv|hh)curveto stack\");\n         f = 0.0;\n         if (sp & 1) { f = s[i]; i++; }\n         for (; i + 3 < sp; i += 4) {\n            if (b0 == 0x1B)\n               stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0);\n            else\n               stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]);\n            f = 0.0;\n         }\n         break;\n\n      case 0x0A: // callsubr\n         if (!has_subrs) {\n            if (info->fdselect.size)\n               subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);\n            has_subrs = 1;\n         }\n         // FALLTHROUGH\n      case 0x1D: // callgsubr\n         if (sp < 1) return STBTT__CSERR(\"call(g|)subr stack\");\n         v = (int) s[--sp];\n         if (subr_stack_height >= 10) return STBTT__CSERR(\"recursion limit\");\n         subr_stack[subr_stack_height++] = b;\n         b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v);\n         if (b.size == 0) return STBTT__CSERR(\"subr not found\");\n         b.cursor = 0;\n         clear_stack = 0;\n         break;\n\n      case 0x0B: // return\n         if (subr_stack_height <= 0) return STBTT__CSERR(\"return outside subr\");\n         b = subr_stack[--subr_stack_height];\n         clear_stack = 0;\n         break;\n\n      case 0x0E: // endchar\n         stbtt__csctx_close_shape(c);\n         return 1;\n\n      case 0x0C: { // two-byte escape\n         float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6;\n         float dx, dy;\n         int b1 = stbtt__buf_get8(&b);\n         switch (b1) {\n         // @TODO These \"flex\" implementations ignore the flex-depth and resolution,\n         // and always draw beziers.\n         case 0x22: // hflex\n            if (sp < 7) return STBTT__CSERR(\"hflex stack\");\n            dx1 = s[0];\n            dx2 = s[1];\n            dy2 = s[2];\n            dx3 = s[3];\n            dx4 = s[4];\n            dx5 = s[5];\n            dx6 = s[6];\n            stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0);\n            stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0);\n            break;\n\n         case 0x23: // flex\n            if (sp < 13) return STBTT__CSERR(\"flex stack\");\n            dx1 = s[0];\n            dy1 = s[1];\n            dx2 = s[2];\n            dy2 = s[3];\n            dx3 = s[4];\n            dy3 = s[5];\n            dx4 = s[6];\n            dy4 = s[7];\n            dx5 = s[8];\n            dy5 = s[9];\n            dx6 = s[10];\n            dy6 = s[11];\n            //fd is s[12]\n            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);\n            stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);\n            break;\n\n         case 0x24: // hflex1\n            if (sp < 9) return STBTT__CSERR(\"hflex1 stack\");\n            dx1 = s[0];\n            dy1 = s[1];\n            dx2 = s[2];\n            dy2 = s[3];\n            dx3 = s[4];\n            dx4 = s[5];\n            dx5 = s[6];\n            dy5 = s[7];\n            dx6 = s[8];\n            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0);\n            stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5));\n            break;\n\n         case 0x25: // flex1\n            if (sp < 11) return STBTT__CSERR(\"flex1 stack\");\n            dx1 = s[0];\n            dy1 = s[1];\n            dx2 = s[2];\n            dy2 = s[3];\n            dx3 = s[4];\n            dy3 = s[5];\n            dx4 = s[6];\n            dy4 = s[7];\n            dx5 = s[8];\n            dy5 = s[9];\n            dx6 = dy6 = s[10];\n            dx = dx1+dx2+dx3+dx4+dx5;\n            dy = dy1+dy2+dy3+dy4+dy5;\n            if (STBTT_fabs(dx) > STBTT_fabs(dy))\n               dy6 = -dy;\n            else\n               dx6 = -dx;\n            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);\n            stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);\n            break;\n\n         default:\n            return STBTT__CSERR(\"unimplemented\");\n         }\n      } break;\n\n      default:\n         if (b0 != 255 && b0 != 28 && b0 < 32)\n            return STBTT__CSERR(\"reserved operator\");\n\n         // push immediate\n         if (b0 == 255) {\n            f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000;\n         } else {\n            stbtt__buf_skip(&b, -1);\n            f = (float)(stbtt_int16)stbtt__cff_int(&b);\n         }\n         if (sp >= 48) return STBTT__CSERR(\"push stack overflow\");\n         s[sp++] = f;\n         clear_stack = 0;\n         break;\n      }\n      if (clear_stack) sp = 0;\n   }\n   return STBTT__CSERR(\"no endchar\");\n\n#undef STBTT__CSERR\n}\n\nstatic int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)\n{\n   // runs the charstring twice, once to count and once to output (to avoid realloc)\n   stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1);\n   stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0);\n   if (stbtt__run_charstring(info, glyph_index, &count_ctx)) {\n      *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata);\n      output_ctx.pvertices = *pvertices;\n      if (stbtt__run_charstring(info, glyph_index, &output_ctx)) {\n         STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices);\n         return output_ctx.num_vertices;\n      }\n   }\n   *pvertices = NULL;\n   return 0;\n}\n\nstatic int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)\n{\n   stbtt__csctx c = STBTT__CSCTX_INIT(1);\n   int r = stbtt__run_charstring(info, glyph_index, &c);\n   if (x0)  *x0 = r ? c.min_x : 0;\n   if (y0)  *y0 = r ? c.min_y : 0;\n   if (x1)  *x1 = r ? c.max_x : 0;\n   if (y1)  *y1 = r ? c.max_y : 0;\n   return r ? c.num_vertices : 0;\n}\n\nSTBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)\n{\n   if (!info->cff.size)\n      return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices);\n   else\n      return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices);\n}\n\nSTBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)\n{\n   stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);\n   if (glyph_index < numOfLongHorMetrics) {\n      if (advanceWidth)     *advanceWidth    = ttSHORT(info->data + info->hmtx + 4*glyph_index);\n      if (leftSideBearing)  *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);\n   } else {\n      if (advanceWidth)     *advanceWidth    = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));\n      if (leftSideBearing)  *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));\n   }\n}\n\nSTBTT_DEF int  stbtt_GetKerningTableLength(const stbtt_fontinfo *info)\n{\n   stbtt_uint8 *data = info->data + info->kern;\n\n   // we only look at the first table. it must be 'horizontal' and format 0.\n   if (!info->kern)\n      return 0;\n   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1\n      return 0;\n   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format\n      return 0;\n\n   return ttUSHORT(data+10);\n}\n\nSTBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length)\n{\n   stbtt_uint8 *data = info->data + info->kern;\n   int k, length;\n\n   // we only look at the first table. it must be 'horizontal' and format 0.\n   if (!info->kern)\n      return 0;\n   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1\n      return 0;\n   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format\n      return 0;\n\n   length = ttUSHORT(data+10);\n   if (table_length < length)\n      length = table_length;\n\n   for (k = 0; k < length; k++)\n   {\n      table[k].glyph1 = ttUSHORT(data+18+(k*6));\n      table[k].glyph2 = ttUSHORT(data+20+(k*6));\n      table[k].advance = ttSHORT(data+22+(k*6));\n   }\n\n   return length;\n}\n\nstatic int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)\n{\n   stbtt_uint8 *data = info->data + info->kern;\n   stbtt_uint32 needle, straw;\n   int l, r, m;\n\n   // we only look at the first table. it must be 'horizontal' and format 0.\n   if (!info->kern)\n      return 0;\n   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1\n      return 0;\n   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format\n      return 0;\n\n   l = 0;\n   r = ttUSHORT(data+10) - 1;\n   needle = glyph1 << 16 | glyph2;\n   while (l <= r) {\n      m = (l + r) >> 1;\n      straw = ttULONG(data+18+(m*6)); // note: unaligned read\n      if (needle < straw)\n         r = m - 1;\n      else if (needle > straw)\n         l = m + 1;\n      else\n         return ttSHORT(data+22+(m*6));\n   }\n   return 0;\n}\n\nstatic stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)\n{\n   stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);\n   switch (coverageFormat) {\n      case 1: {\n         stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);\n\n         // Binary search.\n         stbtt_int32 l=0, r=glyphCount-1, m;\n         int straw, needle=glyph;\n         while (l <= r) {\n            stbtt_uint8 *glyphArray = coverageTable + 4;\n            stbtt_uint16 glyphID;\n            m = (l + r) >> 1;\n            glyphID = ttUSHORT(glyphArray + 2 * m);\n            straw = glyphID;\n            if (needle < straw)\n               r = m - 1;\n            else if (needle > straw)\n               l = m + 1;\n            else {\n               return m;\n            }\n         }\n         break;\n      }\n\n      case 2: {\n         stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);\n         stbtt_uint8 *rangeArray = coverageTable + 4;\n\n         // Binary search.\n         stbtt_int32 l=0, r=rangeCount-1, m;\n         int strawStart, strawEnd, needle=glyph;\n         while (l <= r) {\n            stbtt_uint8 *rangeRecord;\n            m = (l + r) >> 1;\n            rangeRecord = rangeArray + 6 * m;\n            strawStart = ttUSHORT(rangeRecord);\n            strawEnd = ttUSHORT(rangeRecord + 2);\n            if (needle < strawStart)\n               r = m - 1;\n            else if (needle > strawEnd)\n               l = m + 1;\n            else {\n               stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);\n               return startCoverageIndex + glyph - strawStart;\n            }\n         }\n         break;\n      }\n\n      default: return -1; // unsupported\n   }\n\n   return -1;\n}\n\nstatic stbtt_int32  stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)\n{\n   stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);\n   switch (classDefFormat)\n   {\n      case 1: {\n         stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);\n         stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);\n         stbtt_uint8 *classDef1ValueArray = classDefTable + 6;\n\n         if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)\n            return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));\n         break;\n      }\n\n      case 2: {\n         stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);\n         stbtt_uint8 *classRangeRecords = classDefTable + 4;\n\n         // Binary search.\n         stbtt_int32 l=0, r=classRangeCount-1, m;\n         int strawStart, strawEnd, needle=glyph;\n         while (l <= r) {\n            stbtt_uint8 *classRangeRecord;\n            m = (l + r) >> 1;\n            classRangeRecord = classRangeRecords + 6 * m;\n            strawStart = ttUSHORT(classRangeRecord);\n            strawEnd = ttUSHORT(classRangeRecord + 2);\n            if (needle < strawStart)\n               r = m - 1;\n            else if (needle > strawEnd)\n               l = m + 1;\n            else\n               return (stbtt_int32)ttUSHORT(classRangeRecord + 4);\n         }\n         break;\n      }\n\n      default:\n         return -1; // Unsupported definition type, return an error.\n   }\n\n   // \"All glyphs not assigned to a class fall into class 0\". (OpenType spec)\n   return 0;\n}\n\n// Define to STBTT_assert(x) if you want to break on unimplemented formats.\n#define STBTT_GPOS_TODO_assert(x)\n\nstatic stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)\n{\n   stbtt_uint16 lookupListOffset;\n   stbtt_uint8 *lookupList;\n   stbtt_uint16 lookupCount;\n   stbtt_uint8 *data;\n   stbtt_int32 i, sti;\n\n   if (!info->gpos) return 0;\n\n   data = info->data + info->gpos;\n\n   if (ttUSHORT(data+0) != 1) return 0; // Major version 1\n   if (ttUSHORT(data+2) != 0) return 0; // Minor version 0\n\n   lookupListOffset = ttUSHORT(data+8);\n   lookupList = data + lookupListOffset;\n   lookupCount = ttUSHORT(lookupList);\n\n   for (i=0; i<lookupCount; ++i) {\n      stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);\n      stbtt_uint8 *lookupTable = lookupList + lookupOffset;\n\n      stbtt_uint16 lookupType = ttUSHORT(lookupTable);\n      stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);\n      stbtt_uint8 *subTableOffsets = lookupTable + 6;\n      if (lookupType != 2) // Pair Adjustment Positioning Subtable\n         continue;\n\n      for (sti=0; sti<subTableCount; sti++) {\n         stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);\n         stbtt_uint8 *table = lookupTable + subtableOffset;\n         stbtt_uint16 posFormat = ttUSHORT(table);\n         stbtt_uint16 coverageOffset = ttUSHORT(table + 2);\n         stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);\n         if (coverageIndex == -1) continue;\n\n         switch (posFormat) {\n            case 1: {\n               stbtt_int32 l, r, m;\n               int straw, needle;\n               stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);\n               stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);\n               if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?\n                  stbtt_int32 valueRecordPairSizeInBytes = 2;\n                  stbtt_uint16 pairSetCount = ttUSHORT(table + 8);\n                  stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);\n                  stbtt_uint8 *pairValueTable = table + pairPosOffset;\n                  stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);\n                  stbtt_uint8 *pairValueArray = pairValueTable + 2;\n\n                  if (coverageIndex >= pairSetCount) return 0;\n\n                  needle=glyph2;\n                  r=pairValueCount-1;\n                  l=0;\n\n                  // Binary search.\n                  while (l <= r) {\n                     stbtt_uint16 secondGlyph;\n                     stbtt_uint8 *pairValue;\n                     m = (l + r) >> 1;\n                     pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;\n                     secondGlyph = ttUSHORT(pairValue);\n                     straw = secondGlyph;\n                     if (needle < straw)\n                        r = m - 1;\n                     else if (needle > straw)\n                        l = m + 1;\n                     else {\n                        stbtt_int16 xAdvance = ttSHORT(pairValue + 2);\n                        return xAdvance;\n                     }\n                  }\n               } else\n                  return 0;\n               break;\n            }\n\n            case 2: {\n               stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);\n               stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);\n               if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?\n                  stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);\n                  stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);\n                  int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);\n                  int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);\n\n                  stbtt_uint16 class1Count = ttUSHORT(table + 12);\n                  stbtt_uint16 class2Count = ttUSHORT(table + 14);\n                  stbtt_uint8 *class1Records, *class2Records;\n                  stbtt_int16 xAdvance;\n\n                  if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed\n                  if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed\n\n                  class1Records = table + 16;\n                  class2Records = class1Records + 2 * (glyph1class * class2Count);\n                  xAdvance = ttSHORT(class2Records + 2 * glyph2class);\n                  return xAdvance;\n               } else\n                  return 0;\n               break;\n            }\n\n            default:\n               return 0; // Unsupported position format\n         }\n      }\n   }\n\n   return 0;\n}\n\nSTBTT_DEF int  stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)\n{\n   int xAdvance = 0;\n\n   if (info->gpos)\n      xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);\n   else if (info->kern)\n      xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);\n\n   return xAdvance;\n}\n\nSTBTT_DEF int  stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)\n{\n   if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs\n      return 0;\n   return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));\n}\n\nSTBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)\n{\n   stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);\n}\n\nSTBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)\n{\n   if (ascent ) *ascent  = ttSHORT(info->data+info->hhea + 4);\n   if (descent) *descent = ttSHORT(info->data+info->hhea + 6);\n   if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);\n}\n\nSTBTT_DEF int  stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap)\n{\n   int tab = stbtt__find_table(info->data, info->fontstart, \"OS/2\");\n   if (!tab)\n      return 0;\n   if (typoAscent ) *typoAscent  = ttSHORT(info->data+tab + 68);\n   if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70);\n   if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72);\n   return 1;\n}\n\nSTBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)\n{\n   *x0 = ttSHORT(info->data + info->head + 36);\n   *y0 = ttSHORT(info->data + info->head + 38);\n   *x1 = ttSHORT(info->data + info->head + 40);\n   *y1 = ttSHORT(info->data + info->head + 42);\n}\n\nSTBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)\n{\n   int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);\n   return (float) height / fheight;\n}\n\nSTBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)\n{\n   int unitsPerEm = ttUSHORT(info->data + info->head + 18);\n   return pixels / unitsPerEm;\n}\n\nSTBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)\n{\n   STBTT_free(v, info->userdata);\n}\n\nSTBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl)\n{\n   int i;\n   stbtt_uint8 *data = info->data;\n   stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info);\n\n   int numEntries = ttUSHORT(svg_doc_list);\n   stbtt_uint8 *svg_docs = svg_doc_list + 2;\n\n   for(i=0; i<numEntries; i++) {\n      stbtt_uint8 *svg_doc = svg_docs + (12 * i);\n      if ((gl >= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2)))\n         return svg_doc;\n   }\n   return 0;\n}\n\nSTBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg)\n{\n   stbtt_uint8 *data = info->data;\n   stbtt_uint8 *svg_doc;\n\n   if (info->svg == 0)\n      return 0;\n\n   svg_doc = stbtt_FindSVGDoc(info, gl);\n   if (svg_doc != NULL) {\n      *svg = (char *) data + info->svg + ttULONG(svg_doc + 4);\n      return ttULONG(svg_doc + 8);\n   } else {\n      return 0;\n   }\n}\n\nSTBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg)\n{\n   return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// antialiasing software rasterizer\n//\n\nSTBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning\n   if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {\n      // e.g. space character\n      if (ix0) *ix0 = 0;\n      if (iy0) *iy0 = 0;\n      if (ix1) *ix1 = 0;\n      if (iy1) *iy1 = 0;\n   } else {\n      // move to integral bboxes (treating pixels as little squares, what pixels get touched)?\n      if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x);\n      if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y);\n      if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x);\n      if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y);\n   }\n}\n\nSTBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);\n}\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);\n}\n\nSTBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)\n{\n   stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//  Rasterizer\n\ntypedef struct stbtt__hheap_chunk\n{\n   struct stbtt__hheap_chunk *next;\n} stbtt__hheap_chunk;\n\ntypedef struct stbtt__hheap\n{\n   struct stbtt__hheap_chunk *head;\n   void   *first_free;\n   int    num_remaining_in_head_chunk;\n} stbtt__hheap;\n\nstatic void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)\n{\n   if (hh->first_free) {\n      void *p = hh->first_free;\n      hh->first_free = * (void **) p;\n      return p;\n   } else {\n      if (hh->num_remaining_in_head_chunk == 0) {\n         int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);\n         stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata);\n         if (c == NULL)\n            return NULL;\n         c->next = hh->head;\n         hh->head = c;\n         hh->num_remaining_in_head_chunk = count;\n      }\n      --hh->num_remaining_in_head_chunk;\n      return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk;\n   }\n}\n\nstatic void stbtt__hheap_free(stbtt__hheap *hh, void *p)\n{\n   *(void **) p = hh->first_free;\n   hh->first_free = p;\n}\n\nstatic void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)\n{\n   stbtt__hheap_chunk *c = hh->head;\n   while (c) {\n      stbtt__hheap_chunk *n = c->next;\n      STBTT_free(c, userdata);\n      c = n;\n   }\n}\n\ntypedef struct stbtt__edge {\n   float x0,y0, x1,y1;\n   int invert;\n} stbtt__edge;\n\n\ntypedef struct stbtt__active_edge\n{\n   struct stbtt__active_edge *next;\n   #if STBTT_RASTERIZER_VERSION==1\n   int x,dx;\n   float ey;\n   int direction;\n   #elif STBTT_RASTERIZER_VERSION==2\n   float fx,fdx,fdy;\n   float direction;\n   float sy;\n   float ey;\n   #else\n   #error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n   #endif\n} stbtt__active_edge;\n\n#if STBTT_RASTERIZER_VERSION == 1\n#define STBTT_FIXSHIFT   10\n#define STBTT_FIX        (1 << STBTT_FIXSHIFT)\n#define STBTT_FIXMASK    (STBTT_FIX-1)\n\nstatic stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)\n{\n   stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);\n   float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);\n   STBTT_assert(z != NULL);\n   if (!z) return z;\n\n   // round dx down to avoid overshooting\n   if (dxdy < 0)\n      z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);\n   else\n      z->dx = STBTT_ifloor(STBTT_FIX * dxdy);\n\n   z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount\n   z->x -= off_x * STBTT_FIX;\n\n   z->ey = e->y1;\n   z->next = 0;\n   z->direction = e->invert ? 1 : -1;\n   return z;\n}\n#elif STBTT_RASTERIZER_VERSION == 2\nstatic stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)\n{\n   stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);\n   float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);\n   STBTT_assert(z != NULL);\n   //STBTT_assert(e->y0 <= start_point);\n   if (!z) return z;\n   z->fdx = dxdy;\n   z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;\n   z->fx = e->x0 + dxdy * (start_point - e->y0);\n   z->fx -= off_x;\n   z->direction = e->invert ? 1.0f : -1.0f;\n   z->sy = e->y0;\n   z->ey = e->y1;\n   z->next = 0;\n   return z;\n}\n#else\n#error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n#endif\n\n#if STBTT_RASTERIZER_VERSION == 1\n// note: this routine clips fills that extend off the edges... ideally this\n// wouldn't happen, but it could happen if the truetype glyph bounding boxes\n// are wrong, or if the user supplies a too-small bitmap\nstatic void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)\n{\n   // non-zero winding fill\n   int x0=0, w=0;\n\n   while (e) {\n      if (w == 0) {\n         // if we're currently at zero, we need to record the edge start point\n         x0 = e->x; w += e->direction;\n      } else {\n         int x1 = e->x; w += e->direction;\n         // if we went to zero, we need to draw\n         if (w == 0) {\n            int i = x0 >> STBTT_FIXSHIFT;\n            int j = x1 >> STBTT_FIXSHIFT;\n\n            if (i < len && j >= 0) {\n               if (i == j) {\n                  // x0,x1 are the same pixel, so compute combined coverage\n                  scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT);\n               } else {\n                  if (i >= 0) // add antialiasing for x0\n                     scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT);\n                  else\n                     i = -1; // clip\n\n                  if (j < len) // add antialiasing for x1\n                     scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT);\n                  else\n                     j = len; // clip\n\n                  for (++i; i < j; ++i) // fill pixels between x0 and x1\n                     scanline[i] = scanline[i] + (stbtt_uint8) max_weight;\n               }\n            }\n         }\n      }\n\n      e = e->next;\n   }\n}\n\nstatic void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)\n{\n   stbtt__hheap hh = { 0, 0, 0 };\n   stbtt__active_edge *active = NULL;\n   int y,j=0;\n   int max_weight = (255 / vsubsample);  // weight per vertical scanline\n   int s; // vertical subsample index\n   unsigned char scanline_data[512], *scanline;\n\n   if (result->w > 512)\n      scanline = (unsigned char *) STBTT_malloc(result->w, userdata);\n   else\n      scanline = scanline_data;\n\n   y = off_y * vsubsample;\n   e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;\n\n   while (j < result->h) {\n      STBTT_memset(scanline, 0, result->w);\n      for (s=0; s < vsubsample; ++s) {\n         // find center of pixel for this scanline\n         float scan_y = y + 0.5f;\n         stbtt__active_edge **step = &active;\n\n         // update all active edges;\n         // remove all active edges that terminate before the center of this scanline\n         while (*step) {\n            stbtt__active_edge * z = *step;\n            if (z->ey <= scan_y) {\n               *step = z->next; // delete from list\n               STBTT_assert(z->direction);\n               z->direction = 0;\n               stbtt__hheap_free(&hh, z);\n            } else {\n               z->x += z->dx; // advance to position for current scanline\n               step = &((*step)->next); // advance through list\n            }\n         }\n\n         // resort the list if needed\n         for(;;) {\n            int changed=0;\n            step = &active;\n            while (*step && (*step)->next) {\n               if ((*step)->x > (*step)->next->x) {\n                  stbtt__active_edge *t = *step;\n                  stbtt__active_edge *q = t->next;\n\n                  t->next = q->next;\n                  q->next = t;\n                  *step = q;\n                  changed = 1;\n               }\n               step = &(*step)->next;\n            }\n            if (!changed) break;\n         }\n\n         // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline\n         while (e->y0 <= scan_y) {\n            if (e->y1 > scan_y) {\n               stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);\n               if (z != NULL) {\n                  // find insertion point\n                  if (active == NULL)\n                     active = z;\n                  else if (z->x < active->x) {\n                     // insert at front\n                     z->next = active;\n                     active = z;\n                  } else {\n                     // find thing to insert AFTER\n                     stbtt__active_edge *p = active;\n                     while (p->next && p->next->x < z->x)\n                        p = p->next;\n                     // at this point, p->next->x is NOT < z->x\n                     z->next = p->next;\n                     p->next = z;\n                  }\n               }\n            }\n            ++e;\n         }\n\n         // now process all active edges in XOR fashion\n         if (active)\n            stbtt__fill_active_edges(scanline, result->w, active, max_weight);\n\n         ++y;\n      }\n      STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);\n      ++j;\n   }\n\n   stbtt__hheap_cleanup(&hh, userdata);\n\n   if (scanline != scanline_data)\n      STBTT_free(scanline, userdata);\n}\n\n#elif STBTT_RASTERIZER_VERSION == 2\n\n// the edge passed in here does not cross the vertical line at x or the vertical line at x+1\n// (i.e. it has already been clipped to those)\nstatic void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1)\n{\n   if (y0 == y1) return;\n   STBTT_assert(y0 < y1);\n   STBTT_assert(e->sy <= e->ey);\n   if (y0 > e->ey) return;\n   if (y1 < e->sy) return;\n   if (y0 < e->sy) {\n      x0 += (x1-x0) * (e->sy - y0) / (y1-y0);\n      y0 = e->sy;\n   }\n   if (y1 > e->ey) {\n      x1 += (x1-x0) * (e->ey - y1) / (y1-y0);\n      y1 = e->ey;\n   }\n\n   if (x0 == x)\n      STBTT_assert(x1 <= x+1);\n   else if (x0 == x+1)\n      STBTT_assert(x1 >= x);\n   else if (x0 <= x)\n      STBTT_assert(x1 <= x);\n   else if (x0 >= x+1)\n      STBTT_assert(x1 >= x+1);\n   else\n      STBTT_assert(x1 >= x && x1 <= x+1);\n\n   if (x0 <= x && x1 <= x)\n      scanline[x] += e->direction * (y1-y0);\n   else if (x0 >= x+1 && x1 >= x+1)\n      ;\n   else {\n      STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);\n      scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position\n   }\n}\n\nstatic float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)\n{\n   STBTT_assert(top_width >= 0);\n   STBTT_assert(bottom_width >= 0);\n   return (top_width + bottom_width) / 2.0f * height;\n}\n\nstatic float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)\n{\n   return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);\n}\n\nstatic float stbtt__sized_triangle_area(float height, float width)\n{\n   return height * width / 2;\n}\n\nstatic void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)\n{\n   float y_bottom = y_top+1;\n\n   while (e) {\n      // brute force every pixel\n\n      // compute intersection points with top & bottom\n      STBTT_assert(e->ey >= y_top);\n\n      if (e->fdx == 0) {\n         float x0 = e->fx;\n         if (x0 < len) {\n            if (x0 >= 0) {\n               stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);\n               stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);\n            } else {\n               stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);\n            }\n         }\n      } else {\n         float x0 = e->fx;\n         float dx = e->fdx;\n         float xb = x0 + dx;\n         float x_top, x_bottom;\n         float sy0,sy1;\n         float dy = e->fdy;\n         STBTT_assert(e->sy <= y_bottom && e->ey >= y_top);\n\n         // compute endpoints of line segment clipped to this scanline (if the\n         // line segment starts on this scanline. x0 is the intersection of the\n         // line with y_top, but that may be off the line segment.\n         if (e->sy > y_top) {\n            x_top = x0 + dx * (e->sy - y_top);\n            sy0 = e->sy;\n         } else {\n            x_top = x0;\n            sy0 = y_top;\n         }\n         if (e->ey < y_bottom) {\n            x_bottom = x0 + dx * (e->ey - y_top);\n            sy1 = e->ey;\n         } else {\n            x_bottom = xb;\n            sy1 = y_bottom;\n         }\n\n         if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {\n            // from here on, we don't have to range check x values\n\n            if ((int) x_top == (int) x_bottom) {\n               float height;\n               // simple case, only spans one pixel\n               int x = (int) x_top;\n               height = (sy1 - sy0) * e->direction;\n               STBTT_assert(x >= 0 && x < len);\n               scanline[x]      += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);\n               scanline_fill[x] += height; // everything right of this pixel is filled\n            } else {\n               int x,x1,x2;\n               float y_crossing, y_final, step, sign, area;\n               // covers 2+ pixels\n               if (x_top > x_bottom) {\n                  // flip scanline vertically; signed area is the same\n                  float t;\n                  sy0 = y_bottom - (sy0 - y_top);\n                  sy1 = y_bottom - (sy1 - y_top);\n                  t = sy0, sy0 = sy1, sy1 = t;\n                  t = x_bottom, x_bottom = x_top, x_top = t;\n                  dx = -dx;\n                  dy = -dy;\n                  t = x0, x0 = xb, xb = t;\n               }\n               STBTT_assert(dy >= 0);\n               STBTT_assert(dx >= 0);\n\n               x1 = (int) x_top;\n               x2 = (int) x_bottom;\n               // compute intersection with y axis at x1+1\n               y_crossing = y_top + dy * (x1+1 - x0);\n\n               // compute intersection with y axis at x2\n               y_final = y_top + dy * (x2 - x0);\n\n               //           x1    x_top                            x2    x_bottom\n               //     y_top  +------|-----+------------+------------+--------|---+------------+\n               //            |            |            |            |            |            |\n               //            |            |            |            |            |            |\n               //       sy0  |      Txxxxx|............|............|............|............|\n               // y_crossing |            *xxxxx.......|............|............|............|\n               //            |            |     xxxxx..|............|............|............|\n               //            |            |     /-   xx*xxxx........|............|............|\n               //            |            | dy <       |    xxxxxx..|............|............|\n               //   y_final  |            |     \\-     |          xx*xxx.........|............|\n               //       sy1  |            |            |            |   xxxxxB...|............|\n               //            |            |            |            |            |            |\n               //            |            |            |            |            |            |\n               //  y_bottom  +------------+------------+------------+------------+------------+\n               //\n               // goal is to measure the area covered by '.' in each pixel\n\n               // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057\n               // @TODO: maybe test against sy1 rather than y_bottom?\n               if (y_crossing > y_bottom)\n                  y_crossing = y_bottom;\n\n               sign = e->direction;\n\n               // area of the rectangle covered from sy0..y_crossing\n               area = sign * (y_crossing-sy0);\n\n               // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)\n               scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);\n\n               // check if final y_crossing is blown up; no test case for this\n               if (y_final > y_bottom) {\n                  int denom = (x2 - (x1+1));\n                  y_final = y_bottom;\n                  if (denom != 0) { // [DEAR IMGUI] Avoid div by zero (https://github.com/nothings/stb/issues/1316)\n                     dy = (y_final - y_crossing ) / denom; // if denom=0, y_final = y_crossing, so y_final <= y_bottom\n                  }\n               }\n\n               // in second pixel, area covered by line segment found in first pixel\n               // is always a rectangle 1 wide * the height of that line segment; this\n               // is exactly what the variable 'area' stores. it also gets a contribution\n               // from the line segment within it. the THIRD pixel will get the first\n               // pixel's rectangle contribution, the second pixel's rectangle contribution,\n               // and its own contribution. the 'own contribution' is the same in every pixel except\n               // the leftmost and rightmost, a trapezoid that slides down in each pixel.\n               // the second pixel's contribution to the third pixel will be the\n               // rectangle 1 wide times the height change in the second pixel, which is dy.\n\n               step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,\n               // which multiplied by 1-pixel-width is how much pixel area changes for each step in x\n               // so the area advances by 'step' every time\n\n               for (x = x1+1; x < x2; ++x) {\n                  scanline[x] += area + step/2; // area of trapezoid is 1*step/2\n                  area += step;\n               }\n               STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down\n               STBTT_assert(sy1 > y_final-0.01f);\n\n               // area covered in the last pixel is the rectangle from all the pixels to the left,\n               // plus the trapezoid filled by the line segment in this pixel all the way to the right edge\n               scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);\n\n               // the rest of the line is filled based on the total height of the line segment in this pixel\n               scanline_fill[x2] += sign * (sy1-sy0);\n            }\n         } else {\n            // if edge goes outside of box we're drawing, we require\n            // clipping logic. since this does not match the intended use\n            // of this library, we use a different, very slow brute\n            // force implementation\n            // note though that this does happen some of the time because\n            // x_top and x_bottom can be extrapolated at the top & bottom of\n            // the shape and actually lie outside the bounding box\n            int x;\n            for (x=0; x < len; ++x) {\n               // cases:\n               //\n               // there can be up to two intersections with the pixel. any intersection\n               // with left or right edges can be handled by splitting into two (or three)\n               // regions. intersections with top & bottom do not necessitate case-wise logic.\n               //\n               // the old way of doing this found the intersections with the left & right edges,\n               // then used some simple logic to produce up to three segments in sorted order\n               // from top-to-bottom. however, this had a problem: if an x edge was epsilon\n               // across the x border, then the corresponding y position might not be distinct\n               // from the other y segment, and it might ignored as an empty segment. to avoid\n               // that, we need to explicitly produce segments based on x positions.\n\n               // rename variables to clearly-defined pairs\n               float y0 = y_top;\n               float x1 = (float) (x);\n               float x2 = (float) (x+1);\n               float x3 = xb;\n               float y3 = y_bottom;\n\n               // x = e->x + e->dx * (y-y_top)\n               // (y-y_top) = (x - e->x) / e->dx\n               // y = (x - e->x) / e->dx + y_top\n               float y1 = (x - x0) / dx + y_top;\n               float y2 = (x+1 - x0) / dx + y_top;\n\n               if (x0 < x1 && x3 > x2) {         // three segments descending down-right\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);\n               } else if (x3 < x1 && x0 > x2) {  // three segments descending down-left\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);\n               } else if (x0 < x1 && x3 > x1) {  // two segments across x, down-right\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);\n               } else if (x3 < x1 && x0 > x1) {  // two segments across x, down-left\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);\n                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);\n               } else if (x0 < x2 && x3 > x2) {  // two segments across x+1, down-right\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);\n               } else if (x3 < x2 && x0 > x2) {  // two segments across x+1, down-left\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);\n                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);\n               } else {  // one segment\n                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);\n               }\n            }\n         }\n      }\n      e = e->next;\n   }\n}\n\n// directly AA rasterize edges w/o supersampling\nstatic void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)\n{\n   stbtt__hheap hh = { 0, 0, 0 };\n   stbtt__active_edge *active = NULL;\n   int y,j=0, i;\n   float scanline_data[129], *scanline, *scanline2;\n\n   STBTT__NOTUSED(vsubsample);\n\n   if (result->w > 64)\n      scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);\n   else\n      scanline = scanline_data;\n\n   scanline2 = scanline + result->w;\n\n   y = off_y;\n   e[n].y0 = (float) (off_y + result->h) + 1;\n\n   while (j < result->h) {\n      // find center of pixel for this scanline\n      float scan_y_top    = y + 0.0f;\n      float scan_y_bottom = y + 1.0f;\n      stbtt__active_edge **step = &active;\n\n      STBTT_memset(scanline , 0, result->w*sizeof(scanline[0]));\n      STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0]));\n\n      // update all active edges;\n      // remove all active edges that terminate before the top of this scanline\n      while (*step) {\n         stbtt__active_edge * z = *step;\n         if (z->ey <= scan_y_top) {\n            *step = z->next; // delete from list\n            STBTT_assert(z->direction);\n            z->direction = 0;\n            stbtt__hheap_free(&hh, z);\n         } else {\n            step = &((*step)->next); // advance through list\n         }\n      }\n\n      // insert all edges that start before the bottom of this scanline\n      while (e->y0 <= scan_y_bottom) {\n         if (e->y0 != e->y1) {\n            stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);\n            if (z != NULL) {\n               if (j == 0 && off_y != 0) {\n                  if (z->ey < scan_y_top) {\n                     // this can happen due to subpixel positioning and some kind of fp rounding error i think\n                     z->ey = scan_y_top;\n                  }\n               }\n               STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds\n               // insert at front\n               z->next = active;\n               active = z;\n            }\n         }\n         ++e;\n      }\n\n      // now process all active edges\n      if (active)\n         stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);\n\n      {\n         float sum = 0;\n         for (i=0; i < result->w; ++i) {\n            float k;\n            int m;\n            sum += scanline2[i];\n            k = scanline[i] + sum;\n            k = (float) STBTT_fabs(k)*255 + 0.5f;\n            m = (int) k;\n            if (m > 255) m = 255;\n            result->pixels[j*result->stride + i] = (unsigned char) m;\n         }\n      }\n      // advance all the edges\n      step = &active;\n      while (*step) {\n         stbtt__active_edge *z = *step;\n         z->fx += z->fdx; // advance to position for current scanline\n         step = &((*step)->next); // advance through list\n      }\n\n      ++y;\n      ++j;\n   }\n\n   stbtt__hheap_cleanup(&hh, userdata);\n\n   if (scanline != scanline_data)\n      STBTT_free(scanline, userdata);\n}\n#else\n#error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n#endif\n\n#define STBTT__COMPARE(a,b)  ((a)->y0 < (b)->y0)\n\nstatic void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)\n{\n   int i,j;\n   for (i=1; i < n; ++i) {\n      stbtt__edge t = p[i], *a = &t;\n      j = i;\n      while (j > 0) {\n         stbtt__edge *b = &p[j-1];\n         int c = STBTT__COMPARE(a,b);\n         if (!c) break;\n         p[j] = p[j-1];\n         --j;\n      }\n      if (i != j)\n         p[j] = t;\n   }\n}\n\nstatic void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)\n{\n   /* threshold for transitioning to insertion sort */\n   while (n > 12) {\n      stbtt__edge t;\n      int c01,c12,c,m,i,j;\n\n      /* compute median of three */\n      m = n >> 1;\n      c01 = STBTT__COMPARE(&p[0],&p[m]);\n      c12 = STBTT__COMPARE(&p[m],&p[n-1]);\n      /* if 0 >= mid >= end, or 0 < mid < end, then use mid */\n      if (c01 != c12) {\n         /* otherwise, we'll need to swap something else to middle */\n         int z;\n         c = STBTT__COMPARE(&p[0],&p[n-1]);\n         /* 0>mid && mid<n:  0>n => n; 0<n => 0 */\n         /* 0<mid && mid>n:  0>n => 0; 0<n => n */\n         z = (c == c12) ? 0 : n-1;\n         t = p[z];\n         p[z] = p[m];\n         p[m] = t;\n      }\n      /* now p[m] is the median-of-three */\n      /* swap it to the beginning so it won't move around */\n      t = p[0];\n      p[0] = p[m];\n      p[m] = t;\n\n      /* partition loop */\n      i=1;\n      j=n-1;\n      for(;;) {\n         /* handling of equality is crucial here */\n         /* for sentinels & efficiency with duplicates */\n         for (;;++i) {\n            if (!STBTT__COMPARE(&p[i], &p[0])) break;\n         }\n         for (;;--j) {\n            if (!STBTT__COMPARE(&p[0], &p[j])) break;\n         }\n         /* make sure we haven't crossed */\n         if (i >= j) break;\n         t = p[i];\n         p[i] = p[j];\n         p[j] = t;\n\n         ++i;\n         --j;\n      }\n      /* recurse on smaller side, iterate on larger */\n      if (j < (n-i)) {\n         stbtt__sort_edges_quicksort(p,j);\n         p = p+i;\n         n = n-i;\n      } else {\n         stbtt__sort_edges_quicksort(p+i, n-i);\n         n = j;\n      }\n   }\n}\n\nstatic void stbtt__sort_edges(stbtt__edge *p, int n)\n{\n   stbtt__sort_edges_quicksort(p, n);\n   stbtt__sort_edges_ins_sort(p, n);\n}\n\ntypedef struct\n{\n   float x,y;\n} stbtt__point;\n\nstatic void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata)\n{\n   float y_scale_inv = invert ? -scale_y : scale_y;\n   stbtt__edge *e;\n   int n,i,j,k,m;\n#if STBTT_RASTERIZER_VERSION == 1\n   int vsubsample = result->h < 8 ? 15 : 5;\n#elif STBTT_RASTERIZER_VERSION == 2\n   int vsubsample = 1;\n#else\n   #error \"Unrecognized value of STBTT_RASTERIZER_VERSION\"\n#endif\n   // vsubsample should divide 255 evenly; otherwise we won't reach full opacity\n\n   // now we have to blow out the windings into explicit edge lists\n   n = 0;\n   for (i=0; i < windings; ++i)\n      n += wcount[i];\n\n   e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel\n   if (e == 0) return;\n   n = 0;\n\n   m=0;\n   for (i=0; i < windings; ++i) {\n      stbtt__point *p = pts + m;\n      m += wcount[i];\n      j = wcount[i]-1;\n      for (k=0; k < wcount[i]; j=k++) {\n         int a=k,b=j;\n         // skip the edge if horizontal\n         if (p[j].y == p[k].y)\n            continue;\n         // add edge from j to k to the list\n         e[n].invert = 0;\n         if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {\n            e[n].invert = 1;\n            a=j,b=k;\n         }\n         e[n].x0 = p[a].x * scale_x + shift_x;\n         e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample;\n         e[n].x1 = p[b].x * scale_x + shift_x;\n         e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample;\n         ++n;\n      }\n   }\n\n   // now sort the edges by their highest point (should snap to integer, and then by x)\n   //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);\n   stbtt__sort_edges(e, n);\n\n   // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule\n   stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);\n\n   STBTT_free(e, userdata);\n}\n\nstatic void stbtt__add_point(stbtt__point *points, int n, float x, float y)\n{\n   if (!points) return; // during first pass, it's unallocated\n   points[n].x = x;\n   points[n].y = y;\n}\n\n// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching\nstatic int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)\n{\n   // midpoint\n   float mx = (x0 + 2*x1 + x2)/4;\n   float my = (y0 + 2*y1 + y2)/4;\n   // versus directly drawn line\n   float dx = (x0+x2)/2 - mx;\n   float dy = (y0+y2)/2 - my;\n   if (n > 16) // 65536 segments on one curve better be enough!\n      return 1;\n   if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA\n      stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);\n      stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);\n   } else {\n      stbtt__add_point(points, *num_points,x2,y2);\n      *num_points = *num_points+1;\n   }\n   return 1;\n}\n\nstatic void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n)\n{\n   // @TODO this \"flatness\" calculation is just made-up nonsense that seems to work well enough\n   float dx0 = x1-x0;\n   float dy0 = y1-y0;\n   float dx1 = x2-x1;\n   float dy1 = y2-y1;\n   float dx2 = x3-x2;\n   float dy2 = y3-y2;\n   float dx = x3-x0;\n   float dy = y3-y0;\n   float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2));\n   float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy);\n   float flatness_squared = longlen*longlen-shortlen*shortlen;\n\n   if (n > 16) // 65536 segments on one curve better be enough!\n      return;\n\n   if (flatness_squared > objspace_flatness_squared) {\n      float x01 = (x0+x1)/2;\n      float y01 = (y0+y1)/2;\n      float x12 = (x1+x2)/2;\n      float y12 = (y1+y2)/2;\n      float x23 = (x2+x3)/2;\n      float y23 = (y2+y3)/2;\n\n      float xa = (x01+x12)/2;\n      float ya = (y01+y12)/2;\n      float xb = (x12+x23)/2;\n      float yb = (y12+y23)/2;\n\n      float mx = (xa+xb)/2;\n      float my = (ya+yb)/2;\n\n      stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1);\n      stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1);\n   } else {\n      stbtt__add_point(points, *num_points,x3,y3);\n      *num_points = *num_points+1;\n   }\n}\n\n// returns number of contours\nstatic stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)\n{\n   stbtt__point *points=0;\n   int num_points=0;\n\n   float objspace_flatness_squared = objspace_flatness * objspace_flatness;\n   int i,n=0,start=0, pass;\n\n   // count how many \"moves\" there are to get the contour count\n   for (i=0; i < num_verts; ++i)\n      if (vertices[i].type == STBTT_vmove)\n         ++n;\n\n   *num_contours = n;\n   if (n == 0) return 0;\n\n   *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);\n\n   if (*contour_lengths == 0) {\n      *num_contours = 0;\n      return 0;\n   }\n\n   // make two passes through the points so we don't need to realloc\n   for (pass=0; pass < 2; ++pass) {\n      float x=0,y=0;\n      if (pass == 1) {\n         points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);\n         if (points == NULL) goto error;\n      }\n      num_points = 0;\n      n= -1;\n      for (i=0; i < num_verts; ++i) {\n         switch (vertices[i].type) {\n            case STBTT_vmove:\n               // start the next contour\n               if (n >= 0)\n                  (*contour_lengths)[n] = num_points - start;\n               ++n;\n               start = num_points;\n\n               x = vertices[i].x, y = vertices[i].y;\n               stbtt__add_point(points, num_points++, x,y);\n               break;\n            case STBTT_vline:\n               x = vertices[i].x, y = vertices[i].y;\n               stbtt__add_point(points, num_points++, x, y);\n               break;\n            case STBTT_vcurve:\n               stbtt__tesselate_curve(points, &num_points, x,y,\n                                        vertices[i].cx, vertices[i].cy,\n                                        vertices[i].x,  vertices[i].y,\n                                        objspace_flatness_squared, 0);\n               x = vertices[i].x, y = vertices[i].y;\n               break;\n            case STBTT_vcubic:\n               stbtt__tesselate_cubic(points, &num_points, x,y,\n                                        vertices[i].cx, vertices[i].cy,\n                                        vertices[i].cx1, vertices[i].cy1,\n                                        vertices[i].x,  vertices[i].y,\n                                        objspace_flatness_squared, 0);\n               x = vertices[i].x, y = vertices[i].y;\n               break;\n         }\n      }\n      (*contour_lengths)[n] = num_points - start;\n   }\n\n   return points;\nerror:\n   STBTT_free(points, userdata);\n   STBTT_free(*contour_lengths, userdata);\n   *contour_lengths = 0;\n   *num_contours = 0;\n   return NULL;\n}\n\nSTBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)\n{\n   float scale            = scale_x > scale_y ? scale_y : scale_x;\n   int winding_count      = 0;\n   int *winding_lengths   = NULL;\n   stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);\n   if (windings) {\n      stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);\n      STBTT_free(winding_lengths, userdata);\n      STBTT_free(windings, userdata);\n   }\n}\n\nSTBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)\n{\n   STBTT_free(bitmap, userdata);\n}\n\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)\n{\n   int ix0,iy0,ix1,iy1;\n   stbtt__bitmap gbm;\n   stbtt_vertex *vertices;\n   int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);\n\n   if (scale_x == 0) scale_x = scale_y;\n   if (scale_y == 0) {\n      if (scale_x == 0) {\n         STBTT_free(vertices, info->userdata);\n         return NULL;\n      }\n      scale_y = scale_x;\n   }\n\n   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);\n\n   // now we get the size\n   gbm.w = (ix1 - ix0);\n   gbm.h = (iy1 - iy0);\n   gbm.pixels = NULL; // in case we error\n\n   if (width ) *width  = gbm.w;\n   if (height) *height = gbm.h;\n   if (xoff  ) *xoff   = ix0;\n   if (yoff  ) *yoff   = iy0;\n\n   if (gbm.w && gbm.h) {\n      gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);\n      if (gbm.pixels) {\n         gbm.stride = gbm.w;\n\n         stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata);\n      }\n   }\n   STBTT_free(vertices, info->userdata);\n   return gbm.pixels;\n}\n\nSTBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);\n}\n\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)\n{\n   int ix0,iy0;\n   stbtt_vertex *vertices;\n   int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);\n   stbtt__bitmap gbm;\n\n   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);\n   gbm.pixels = output;\n   gbm.w = out_w;\n   gbm.h = out_h;\n   gbm.stride = out_stride;\n\n   if (gbm.w && gbm.h)\n      stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata);\n\n   STBTT_free(vertices, info->userdata);\n}\n\nSTBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)\n{\n   stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);\n}\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);\n}\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint)\n{\n   stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint));\n}\n\nSTBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)\n{\n   stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));\n}\n\nSTBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);\n}\n\nSTBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)\n{\n   stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// bitmap baking\n//\n// This is SUPER-CRAPPY packing to keep source code small\n\nstatic int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset,  // font location (use offset=0 for plain .ttf)\n                                float pixel_height,                     // height of font in pixels\n                                unsigned char *pixels, int pw, int ph,  // bitmap to be filled in\n                                int first_char, int num_chars,          // characters to bake\n                                stbtt_bakedchar *chardata)\n{\n   float scale;\n   int x,y,bottom_y, i;\n   stbtt_fontinfo f;\n   f.userdata = NULL;\n   if (!stbtt_InitFont(&f, data, offset))\n      return -1;\n   STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels\n   x=y=1;\n   bottom_y = 1;\n\n   scale = stbtt_ScaleForPixelHeight(&f, pixel_height);\n\n   for (i=0; i < num_chars; ++i) {\n      int advance, lsb, x0,y0,x1,y1,gw,gh;\n      int g = stbtt_FindGlyphIndex(&f, first_char + i);\n      stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);\n      stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);\n      gw = x1-x0;\n      gh = y1-y0;\n      if (x + gw + 1 >= pw)\n         y = bottom_y, x = 1; // advance to next row\n      if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row\n         return -i;\n      STBTT_assert(x+gw < pw);\n      STBTT_assert(y+gh < ph);\n      stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);\n      chardata[i].x0 = (stbtt_int16) x;\n      chardata[i].y0 = (stbtt_int16) y;\n      chardata[i].x1 = (stbtt_int16) (x + gw);\n      chardata[i].y1 = (stbtt_int16) (y + gh);\n      chardata[i].xadvance = scale * advance;\n      chardata[i].xoff     = (float) x0;\n      chardata[i].yoff     = (float) y0;\n      x = x + gw + 1;\n      if (y+gh+1 > bottom_y)\n         bottom_y = y+gh+1;\n   }\n   return bottom_y;\n}\n\nSTBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)\n{\n   float d3d_bias = opengl_fillrule ? 0 : -0.5f;\n   float ipw = 1.0f / pw, iph = 1.0f / ph;\n   const stbtt_bakedchar *b = chardata + char_index;\n   int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);\n   int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);\n\n   q->x0 = round_x + d3d_bias;\n   q->y0 = round_y + d3d_bias;\n   q->x1 = round_x + b->x1 - b->x0 + d3d_bias;\n   q->y1 = round_y + b->y1 - b->y0 + d3d_bias;\n\n   q->s0 = b->x0 * ipw;\n   q->t0 = b->y0 * iph;\n   q->s1 = b->x1 * ipw;\n   q->t1 = b->y1 * iph;\n\n   *xpos += b->xadvance;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// rectangle packing replacement routines if you don't have stb_rect_pack.h\n//\n\n#ifndef STB_RECT_PACK_VERSION\n\ntypedef int stbrp_coord;\n\n////////////////////////////////////////////////////////////////////////////////////\n//                                                                                //\n//                                                                                //\n// COMPILER WARNING ?!?!?                                                         //\n//                                                                                //\n//                                                                                //\n// if you get a compile warning due to these symbols being defined more than      //\n// once, move #include \"stb_rect_pack.h\" before #include \"stb_truetype.h\"         //\n//                                                                                //\n////////////////////////////////////////////////////////////////////////////////////\n\ntypedef struct\n{\n   int width,height;\n   int x,y,bottom_y;\n} stbrp_context;\n\ntypedef struct\n{\n   unsigned char x;\n} stbrp_node;\n\nstruct stbrp_rect\n{\n   stbrp_coord x,y;\n   int id,w,h,was_packed;\n};\n\nstatic void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)\n{\n   con->width  = pw;\n   con->height = ph;\n   con->x = 0;\n   con->y = 0;\n   con->bottom_y = 0;\n   STBTT__NOTUSED(nodes);\n   STBTT__NOTUSED(num_nodes);\n}\n\nstatic void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)\n{\n   int i;\n   for (i=0; i < num_rects; ++i) {\n      if (con->x + rects[i].w > con->width) {\n         con->x = 0;\n         con->y = con->bottom_y;\n      }\n      if (con->y + rects[i].h > con->height)\n         break;\n      rects[i].x = con->x;\n      rects[i].y = con->y;\n      rects[i].was_packed = 1;\n      con->x += rects[i].w;\n      if (con->y + rects[i].h > con->bottom_y)\n         con->bottom_y = con->y + rects[i].h;\n   }\n   for (   ; i < num_rects; ++i)\n      rects[i].was_packed = 0;\n}\n#endif\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// bitmap baking\n//\n// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If\n// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.\n\nSTBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)\n{\n   stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context)            ,alloc_context);\n   int            num_nodes = pw - padding;\n   stbrp_node    *nodes   = (stbrp_node    *) STBTT_malloc(sizeof(*nodes  ) * num_nodes,alloc_context);\n\n   if (context == NULL || nodes == NULL) {\n      if (context != NULL) STBTT_free(context, alloc_context);\n      if (nodes   != NULL) STBTT_free(nodes  , alloc_context);\n      return 0;\n   }\n\n   spc->user_allocator_context = alloc_context;\n   spc->width = pw;\n   spc->height = ph;\n   spc->pixels = pixels;\n   spc->pack_info = context;\n   spc->nodes = nodes;\n   spc->padding = padding;\n   spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;\n   spc->h_oversample = 1;\n   spc->v_oversample = 1;\n   spc->skip_missing = 0;\n\n   stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);\n\n   if (pixels)\n      STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels\n\n   return 1;\n}\n\nSTBTT_DEF void stbtt_PackEnd  (stbtt_pack_context *spc)\n{\n   STBTT_free(spc->nodes    , spc->user_allocator_context);\n   STBTT_free(spc->pack_info, spc->user_allocator_context);\n}\n\nSTBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)\n{\n   STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);\n   STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);\n   if (h_oversample <= STBTT_MAX_OVERSAMPLE)\n      spc->h_oversample = h_oversample;\n   if (v_oversample <= STBTT_MAX_OVERSAMPLE)\n      spc->v_oversample = v_oversample;\n}\n\nSTBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip)\n{\n   spc->skip_missing = skip;\n}\n\n#define STBTT__OVER_MASK  (STBTT_MAX_OVERSAMPLE-1)\n\nstatic void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)\n{\n   unsigned char buffer[STBTT_MAX_OVERSAMPLE];\n   int safe_w = w - kernel_width;\n   int j;\n   STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze\n   for (j=0; j < h; ++j) {\n      int i;\n      unsigned int total;\n      STBTT_memset(buffer, 0, kernel_width);\n\n      total = 0;\n\n      // make kernel_width a constant in common cases so compiler can optimize out the divide\n      switch (kernel_width) {\n         case 2:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 2);\n            }\n            break;\n         case 3:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 3);\n            }\n            break;\n         case 4:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 4);\n            }\n            break;\n         case 5:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / 5);\n            }\n            break;\n         default:\n            for (i=0; i <= safe_w; ++i) {\n               total += pixels[i] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];\n               pixels[i] = (unsigned char) (total / kernel_width);\n            }\n            break;\n      }\n\n      for (; i < w; ++i) {\n         STBTT_assert(pixels[i] == 0);\n         total -= buffer[i & STBTT__OVER_MASK];\n         pixels[i] = (unsigned char) (total / kernel_width);\n      }\n\n      pixels += stride_in_bytes;\n   }\n}\n\nstatic void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)\n{\n   unsigned char buffer[STBTT_MAX_OVERSAMPLE];\n   int safe_h = h - kernel_width;\n   int j;\n   STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze\n   for (j=0; j < w; ++j) {\n      int i;\n      unsigned int total;\n      STBTT_memset(buffer, 0, kernel_width);\n\n      total = 0;\n\n      // make kernel_width a constant in common cases so compiler can optimize out the divide\n      switch (kernel_width) {\n         case 2:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 2);\n            }\n            break;\n         case 3:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 3);\n            }\n            break;\n         case 4:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 4);\n            }\n            break;\n         case 5:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / 5);\n            }\n            break;\n         default:\n            for (i=0; i <= safe_h; ++i) {\n               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];\n               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];\n               pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);\n            }\n            break;\n      }\n\n      for (; i < h; ++i) {\n         STBTT_assert(pixels[i*stride_in_bytes] == 0);\n         total -= buffer[i & STBTT__OVER_MASK];\n         pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);\n      }\n\n      pixels += 1;\n   }\n}\n\nstatic float stbtt__oversample_shift(int oversample)\n{\n   if (!oversample)\n      return 0.0f;\n\n   // The prefilter is a box filter of width \"oversample\",\n   // which shifts phase by (oversample - 1)/2 pixels in\n   // oversampled space. We want to shift in the opposite\n   // direction to counter this.\n   return (float)-(oversample - 1) / (2.0f * (float)oversample);\n}\n\n// rects array must be big enough to accommodate all characters in the given ranges\nSTBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)\n{\n   int i,j,k;\n   int missing_glyph_added = 0;\n\n   k=0;\n   for (i=0; i < num_ranges; ++i) {\n      float fh = ranges[i].font_size;\n      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);\n      ranges[i].h_oversample = (unsigned char) spc->h_oversample;\n      ranges[i].v_oversample = (unsigned char) spc->v_oversample;\n      for (j=0; j < ranges[i].num_chars; ++j) {\n         int x0,y0,x1,y1;\n         int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];\n         int glyph = stbtt_FindGlyphIndex(info, codepoint);\n         if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) {\n            rects[k].w = rects[k].h = 0;\n         } else {\n            stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,\n                                            scale * spc->h_oversample,\n                                            scale * spc->v_oversample,\n                                            0,0,\n                                            &x0,&y0,&x1,&y1);\n            rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);\n            rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);\n            if (glyph == 0)\n               missing_glyph_added = 1;\n         }\n         ++k;\n      }\n   }\n\n   return k;\n}\n\nSTBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph)\n{\n   stbtt_MakeGlyphBitmapSubpixel(info,\n                                 output,\n                                 out_w - (prefilter_x - 1),\n                                 out_h - (prefilter_y - 1),\n                                 out_stride,\n                                 scale_x,\n                                 scale_y,\n                                 shift_x,\n                                 shift_y,\n                                 glyph);\n\n   if (prefilter_x > 1)\n      stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x);\n\n   if (prefilter_y > 1)\n      stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y);\n\n   *sub_x = stbtt__oversample_shift(prefilter_x);\n   *sub_y = stbtt__oversample_shift(prefilter_y);\n}\n\n// rects array must be big enough to accommodate all characters in the given ranges\nSTBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)\n{\n   int i,j,k, missing_glyph = -1, return_value = 1;\n\n   // save current values\n   int old_h_over = spc->h_oversample;\n   int old_v_over = spc->v_oversample;\n\n   k = 0;\n   for (i=0; i < num_ranges; ++i) {\n      float fh = ranges[i].font_size;\n      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);\n      float recip_h,recip_v,sub_x,sub_y;\n      spc->h_oversample = ranges[i].h_oversample;\n      spc->v_oversample = ranges[i].v_oversample;\n      recip_h = 1.0f / spc->h_oversample;\n      recip_v = 1.0f / spc->v_oversample;\n      sub_x = stbtt__oversample_shift(spc->h_oversample);\n      sub_y = stbtt__oversample_shift(spc->v_oversample);\n      for (j=0; j < ranges[i].num_chars; ++j) {\n         stbrp_rect *r = &rects[k];\n         if (r->was_packed && r->w != 0 && r->h != 0) {\n            stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];\n            int advance, lsb, x0,y0,x1,y1;\n            int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];\n            int glyph = stbtt_FindGlyphIndex(info, codepoint);\n            stbrp_coord pad = (stbrp_coord) spc->padding;\n\n            // pad on left and top\n            r->x += pad;\n            r->y += pad;\n            r->w -= pad;\n            r->h -= pad;\n            stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);\n            stbtt_GetGlyphBitmapBox(info, glyph,\n                                    scale * spc->h_oversample,\n                                    scale * spc->v_oversample,\n                                    &x0,&y0,&x1,&y1);\n            stbtt_MakeGlyphBitmapSubpixel(info,\n                                          spc->pixels + r->x + r->y*spc->stride_in_bytes,\n                                          r->w - spc->h_oversample+1,\n                                          r->h - spc->v_oversample+1,\n                                          spc->stride_in_bytes,\n                                          scale * spc->h_oversample,\n                                          scale * spc->v_oversample,\n                                          0,0,\n                                          glyph);\n\n            if (spc->h_oversample > 1)\n               stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,\n                                  r->w, r->h, spc->stride_in_bytes,\n                                  spc->h_oversample);\n\n            if (spc->v_oversample > 1)\n               stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,\n                                  r->w, r->h, spc->stride_in_bytes,\n                                  spc->v_oversample);\n\n            bc->x0       = (stbtt_int16)  r->x;\n            bc->y0       = (stbtt_int16)  r->y;\n            bc->x1       = (stbtt_int16) (r->x + r->w);\n            bc->y1       = (stbtt_int16) (r->y + r->h);\n            bc->xadvance =                scale * advance;\n            bc->xoff     =       (float)  x0 * recip_h + sub_x;\n            bc->yoff     =       (float)  y0 * recip_v + sub_y;\n            bc->xoff2    =                (x0 + r->w) * recip_h + sub_x;\n            bc->yoff2    =                (y0 + r->h) * recip_v + sub_y;\n\n            if (glyph == 0)\n               missing_glyph = j;\n         } else if (spc->skip_missing) {\n            return_value = 0;\n         } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) {\n            ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph];\n         } else {\n            return_value = 0; // if any fail, report failure\n         }\n\n         ++k;\n      }\n   }\n\n   // restore original values\n   spc->h_oversample = old_h_over;\n   spc->v_oversample = old_v_over;\n\n   return return_value;\n}\n\nSTBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects)\n{\n   stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);\n}\n\nSTBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)\n{\n   stbtt_fontinfo info;\n   int i, j, n, return_value; // [DEAR IMGUI] removed = 1;\n   //stbrp_context *context = (stbrp_context *) spc->pack_info;\n   stbrp_rect    *rects;\n\n   // flag all characters as NOT packed\n   for (i=0; i < num_ranges; ++i)\n      for (j=0; j < ranges[i].num_chars; ++j)\n         ranges[i].chardata_for_range[j].x0 =\n         ranges[i].chardata_for_range[j].y0 =\n         ranges[i].chardata_for_range[j].x1 =\n         ranges[i].chardata_for_range[j].y1 = 0;\n\n   n = 0;\n   for (i=0; i < num_ranges; ++i)\n      n += ranges[i].num_chars;\n\n   rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);\n   if (rects == NULL)\n      return 0;\n\n   info.userdata = spc->user_allocator_context;\n   stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));\n\n   n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);\n\n   stbtt_PackFontRangesPackRects(spc, rects, n);\n\n   return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);\n\n   STBTT_free(rects, spc->user_allocator_context);\n   return return_value;\n}\n\nSTBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,\n            int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)\n{\n   stbtt_pack_range range;\n   range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range;\n   range.array_of_unicode_codepoints = NULL;\n   range.num_chars                   = num_chars_in_range;\n   range.chardata_for_range          = chardata_for_range;\n   range.font_size                   = font_size;\n   return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);\n}\n\nSTBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap)\n{\n   int i_ascent, i_descent, i_lineGap;\n   float scale;\n   stbtt_fontinfo info;\n   stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index));\n   scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size);\n   stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap);\n   *ascent  = (float) i_ascent  * scale;\n   *descent = (float) i_descent * scale;\n   *lineGap = (float) i_lineGap * scale;\n}\n\nSTBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)\n{\n   float ipw = 1.0f / pw, iph = 1.0f / ph;\n   const stbtt_packedchar *b = chardata + char_index;\n\n   if (align_to_integer) {\n      float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);\n      float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);\n      q->x0 = x;\n      q->y0 = y;\n      q->x1 = x + b->xoff2 - b->xoff;\n      q->y1 = y + b->yoff2 - b->yoff;\n   } else {\n      q->x0 = *xpos + b->xoff;\n      q->y0 = *ypos + b->yoff;\n      q->x1 = *xpos + b->xoff2;\n      q->y1 = *ypos + b->yoff2;\n   }\n\n   q->s0 = b->x0 * ipw;\n   q->t0 = b->y0 * iph;\n   q->s1 = b->x1 * ipw;\n   q->t1 = b->y1 * iph;\n\n   *xpos += b->xadvance;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// sdf computation\n//\n\n#define STBTT_min(a,b)  ((a) < (b) ? (a) : (b))\n#define STBTT_max(a,b)  ((a) < (b) ? (b) : (a))\n\nstatic int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2])\n{\n   float q0perp = q0[1]*ray[0] - q0[0]*ray[1];\n   float q1perp = q1[1]*ray[0] - q1[0]*ray[1];\n   float q2perp = q2[1]*ray[0] - q2[0]*ray[1];\n   float roperp = orig[1]*ray[0] - orig[0]*ray[1];\n\n   float a = q0perp - 2*q1perp + q2perp;\n   float b = q1perp - q0perp;\n   float c = q0perp - roperp;\n\n   float s0 = 0., s1 = 0.;\n   int num_s = 0;\n\n   if (a != 0.0) {\n      float discr = b*b - a*c;\n      if (discr > 0.0) {\n         float rcpna = -1 / a;\n         float d = (float) STBTT_sqrt(discr);\n         s0 = (b+d) * rcpna;\n         s1 = (b-d) * rcpna;\n         if (s0 >= 0.0 && s0 <= 1.0)\n            num_s = 1;\n         if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) {\n            if (num_s == 0) s0 = s1;\n            ++num_s;\n         }\n      }\n   } else {\n      // 2*b*s + c = 0\n      // s = -c / (2*b)\n      s0 = c / (-2 * b);\n      if (s0 >= 0.0 && s0 <= 1.0)\n         num_s = 1;\n   }\n\n   if (num_s == 0)\n      return 0;\n   else {\n      float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]);\n      float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2;\n\n      float q0d =   q0[0]*rayn_x +   q0[1]*rayn_y;\n      float q1d =   q1[0]*rayn_x +   q1[1]*rayn_y;\n      float q2d =   q2[0]*rayn_x +   q2[1]*rayn_y;\n      float rod = orig[0]*rayn_x + orig[1]*rayn_y;\n\n      float q10d = q1d - q0d;\n      float q20d = q2d - q0d;\n      float q0rd = q0d - rod;\n\n      hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d;\n      hits[0][1] = a*s0+b;\n\n      if (num_s > 1) {\n         hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d;\n         hits[1][1] = a*s1+b;\n         return 2;\n      } else {\n         return 1;\n      }\n   }\n}\n\nstatic int equal(float *a, float *b)\n{\n   return (a[0] == b[0] && a[1] == b[1]);\n}\n\nstatic int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts)\n{\n   int i;\n   float orig[2], ray[2] = { 1, 0 };\n   float y_frac;\n   int winding = 0;\n\n   // make sure y never passes through a vertex of the shape\n   y_frac = (float) STBTT_fmod(y, 1.0f);\n   if (y_frac < 0.01f)\n      y += 0.01f;\n   else if (y_frac > 0.99f)\n      y -= 0.01f;\n\n   orig[0] = x;\n   orig[1] = y;\n\n   // test a ray from (-infinity,y) to (x,y)\n   for (i=0; i < nverts; ++i) {\n      if (verts[i].type == STBTT_vline) {\n         int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y;\n         int x1 = (int) verts[i  ].x, y1 = (int) verts[i  ].y;\n         if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {\n            float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;\n            if (x_inter < x)\n               winding += (y0 < y1) ? 1 : -1;\n         }\n      }\n      if (verts[i].type == STBTT_vcurve) {\n         int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ;\n         int x1 = (int) verts[i  ].cx, y1 = (int) verts[i  ].cy;\n         int x2 = (int) verts[i  ].x , y2 = (int) verts[i  ].y ;\n         int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2));\n         int by = STBTT_max(y0,STBTT_max(y1,y2));\n         if (y > ay && y < by && x > ax) {\n            float q0[2],q1[2],q2[2];\n            float hits[2][2];\n            q0[0] = (float)x0;\n            q0[1] = (float)y0;\n            q1[0] = (float)x1;\n            q1[1] = (float)y1;\n            q2[0] = (float)x2;\n            q2[1] = (float)y2;\n            if (equal(q0,q1) || equal(q1,q2)) {\n               x0 = (int)verts[i-1].x;\n               y0 = (int)verts[i-1].y;\n               x1 = (int)verts[i  ].x;\n               y1 = (int)verts[i  ].y;\n               if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {\n                  float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;\n                  if (x_inter < x)\n                     winding += (y0 < y1) ? 1 : -1;\n               }\n            } else {\n               int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits);\n               if (num_hits >= 1)\n                  if (hits[0][0] < 0)\n                     winding += (hits[0][1] < 0 ? -1 : 1);\n               if (num_hits >= 2)\n                  if (hits[1][0] < 0)\n                     winding += (hits[1][1] < 0 ? -1 : 1);\n            }\n         }\n      }\n   }\n   return winding;\n}\n\nstatic float stbtt__cuberoot( float x )\n{\n   if (x<0)\n      return -(float) STBTT_pow(-x,1.0f/3.0f);\n   else\n      return  (float) STBTT_pow( x,1.0f/3.0f);\n}\n\n// x^3 + a*x^2 + b*x + c = 0\nstatic int stbtt__solve_cubic(float a, float b, float c, float* r)\n{\n   float s = -a / 3;\n   float p = b - a*a / 3;\n   float q = a * (2*a*a - 9*b) / 27 + c;\n   float p3 = p*p*p;\n   float d = q*q + 4*p3 / 27;\n   if (d >= 0) {\n      float z = (float) STBTT_sqrt(d);\n      float u = (-q + z) / 2;\n      float v = (-q - z) / 2;\n      u = stbtt__cuberoot(u);\n      v = stbtt__cuberoot(v);\n      r[0] = s + u + v;\n      return 1;\n   } else {\n      float u = (float) STBTT_sqrt(-p/3);\n      float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative\n      float m = (float) STBTT_cos(v);\n      float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;\n      r[0] = s + u * 2 * m;\n      r[1] = s - u * (m + n);\n      r[2] = s - u * (m - n);\n\n      //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f);  // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?\n      //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);\n      //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);\n      return 3;\n   }\n}\n\nSTBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)\n{\n   float scale_x = scale, scale_y = scale;\n   int ix0,iy0,ix1,iy1;\n   int w,h;\n   unsigned char *data;\n\n   if (scale == 0) return NULL;\n\n   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1);\n\n   // if empty, return NULL\n   if (ix0 == ix1 || iy0 == iy1)\n      return NULL;\n\n   ix0 -= padding;\n   iy0 -= padding;\n   ix1 += padding;\n   iy1 += padding;\n\n   w = (ix1 - ix0);\n   h = (iy1 - iy0);\n\n   if (width ) *width  = w;\n   if (height) *height = h;\n   if (xoff  ) *xoff   = ix0;\n   if (yoff  ) *yoff   = iy0;\n\n   // invert for y-downwards bitmaps\n   scale_y = -scale_y;\n\n   {\n      int x,y,i,j;\n      float *precompute;\n      stbtt_vertex *verts;\n      int num_verts = stbtt_GetGlyphShape(info, glyph, &verts);\n      data = (unsigned char *) STBTT_malloc(w * h, info->userdata);\n      precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata);\n\n      for (i=0,j=num_verts-1; i < num_verts; j=i++) {\n         if (verts[i].type == STBTT_vline) {\n            float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;\n            float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;\n            float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));\n            precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist;\n         } else if (verts[i].type == STBTT_vcurve) {\n            float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;\n            float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;\n            float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;\n            float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;\n            float len2 = bx*bx + by*by;\n            if (len2 != 0.0f)\n               precompute[i] = 1.0f / (bx*bx + by*by);\n            else\n               precompute[i] = 0.0f;\n         } else\n            precompute[i] = 0.0f;\n      }\n\n      for (y=iy0; y < iy1; ++y) {\n         for (x=ix0; x < ix1; ++x) {\n            float val;\n            float min_dist = 999999.0f;\n            float sx = (float) x + 0.5f;\n            float sy = (float) y + 0.5f;\n            float x_gspace = (sx / scale_x);\n            float y_gspace = (sy / scale_y);\n\n            int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path\n\n            for (i=0; i < num_verts; ++i) {\n               float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;\n\n               if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) {\n                  float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y;\n\n                  float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);\n                  if (dist2 < min_dist*min_dist)\n                     min_dist = (float) STBTT_sqrt(dist2);\n\n                  // coarse culling against bbox\n                  //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist &&\n                  //    sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist)\n                  dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];\n                  STBTT_assert(i != 0);\n                  if (dist < min_dist) {\n                     // check position along line\n                     // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0)\n                     // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy)\n                     float dx = x1-x0, dy = y1-y0;\n                     float px = x0-sx, py = y0-sy;\n                     // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy\n                     // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve\n                     float t = -(px*dx + py*dy) / (dx*dx + dy*dy);\n                     if (t >= 0.0f && t <= 1.0f)\n                        min_dist = dist;\n                  }\n               } else if (verts[i].type == STBTT_vcurve) {\n                  float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y;\n                  float x1 = verts[i  ].cx*scale_x, y1 = verts[i  ].cy*scale_y;\n                  float box_x0 = STBTT_min(STBTT_min(x0,x1),x2);\n                  float box_y0 = STBTT_min(STBTT_min(y0,y1),y2);\n                  float box_x1 = STBTT_max(STBTT_max(x0,x1),x2);\n                  float box_y1 = STBTT_max(STBTT_max(y0,y1),y2);\n                  // coarse culling against bbox to avoid computing cubic unnecessarily\n                  if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) {\n                     int num=0;\n                     float ax = x1-x0, ay = y1-y0;\n                     float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;\n                     float mx = x0 - sx, my = y0 - sy;\n                     float res[3] = {0.f,0.f,0.f};\n                     float px,py,t,it,dist2;\n                     float a_inv = precompute[i];\n                     if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula\n                        float a = 3*(ax*bx + ay*by);\n                        float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);\n                        float c = mx*ax+my*ay;\n                        if (a == 0.0) { // if a is 0, it's linear\n                           if (b != 0.0) {\n                              res[num++] = -c/b;\n                           }\n                        } else {\n                           float discriminant = b*b - 4*a*c;\n                           if (discriminant < 0)\n                              num = 0;\n                           else {\n                              float root = (float) STBTT_sqrt(discriminant);\n                              res[0] = (-b - root)/(2*a);\n                              res[1] = (-b + root)/(2*a);\n                              num = 2; // don't bother distinguishing 1-solution case, as code below will still work\n                           }\n                        }\n                     } else {\n                        float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point\n                        float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv;\n                        float d = (mx*ax+my*ay) * a_inv;\n                        num = stbtt__solve_cubic(b, c, d, res);\n                     }\n                     dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);\n                     if (dist2 < min_dist*min_dist)\n                        min_dist = (float) STBTT_sqrt(dist2);\n\n                     if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {\n                        t = res[0], it = 1.0f - t;\n                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;\n                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;\n                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);\n                        if (dist2 < min_dist * min_dist)\n                           min_dist = (float) STBTT_sqrt(dist2);\n                     }\n                     if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) {\n                        t = res[1], it = 1.0f - t;\n                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;\n                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;\n                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);\n                        if (dist2 < min_dist * min_dist)\n                           min_dist = (float) STBTT_sqrt(dist2);\n                     }\n                     if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) {\n                        t = res[2], it = 1.0f - t;\n                        px = it*it*x0 + 2*t*it*x1 + t*t*x2;\n                        py = it*it*y0 + 2*t*it*y1 + t*t*y2;\n                        dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);\n                        if (dist2 < min_dist * min_dist)\n                           min_dist = (float) STBTT_sqrt(dist2);\n                     }\n                  }\n               }\n            }\n            if (winding == 0)\n               min_dist = -min_dist;  // if outside the shape, value is negative\n            val = onedge_value + pixel_dist_scale * min_dist;\n            if (val < 0)\n               val = 0;\n            else if (val > 255)\n               val = 255;\n            data[(y-iy0)*w+(x-ix0)] = (unsigned char) val;\n         }\n      }\n      STBTT_free(precompute, info->userdata);\n      STBTT_free(verts, info->userdata);\n   }\n   return data;\n}\n\nSTBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)\n{\n   return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff);\n}\n\nSTBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata)\n{\n   STBTT_free(bitmap, userdata);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// font name matching -- recommended not to use this\n//\n\n// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string\nstatic stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)\n{\n   stbtt_int32 i=0;\n\n   // convert utf16 to utf8 and compare the results while converting\n   while (len2) {\n      stbtt_uint16 ch = s2[0]*256 + s2[1];\n      if (ch < 0x80) {\n         if (i >= len1) return -1;\n         if (s1[i++] != ch) return -1;\n      } else if (ch < 0x800) {\n         if (i+1 >= len1) return -1;\n         if (s1[i++] != 0xc0 + (ch >> 6)) return -1;\n         if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;\n      } else if (ch >= 0xd800 && ch < 0xdc00) {\n         stbtt_uint32 c;\n         stbtt_uint16 ch2 = s2[2]*256 + s2[3];\n         if (i+3 >= len1) return -1;\n         c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;\n         if (s1[i++] != 0xf0 + (c >> 18)) return -1;\n         if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;\n         if (s1[i++] != 0x80 + ((c >>  6) & 0x3f)) return -1;\n         if (s1[i++] != 0x80 + ((c      ) & 0x3f)) return -1;\n         s2 += 2; // plus another 2 below\n         len2 -= 2;\n      } else if (ch >= 0xdc00 && ch < 0xe000) {\n         return -1;\n      } else {\n         if (i+2 >= len1) return -1;\n         if (s1[i++] != 0xe0 + (ch >> 12)) return -1;\n         if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;\n         if (s1[i++] != 0x80 + ((ch     ) & 0x3f)) return -1;\n      }\n      s2 += 2;\n      len2 -= 2;\n   }\n   return i;\n}\n\nstatic int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2)\n{\n   return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);\n}\n\n// returns results in whatever encoding you request... but note that 2-byte encodings\n// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare\nSTBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)\n{\n   stbtt_int32 i,count,stringOffset;\n   stbtt_uint8 *fc = font->data;\n   stbtt_uint32 offset = font->fontstart;\n   stbtt_uint32 nm = stbtt__find_table(fc, offset, \"name\");\n   if (!nm) return NULL;\n\n   count = ttUSHORT(fc+nm+2);\n   stringOffset = nm + ttUSHORT(fc+nm+4);\n   for (i=0; i < count; ++i) {\n      stbtt_uint32 loc = nm + 6 + 12 * i;\n      if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)\n          && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {\n         *length = ttUSHORT(fc+loc+8);\n         return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10));\n      }\n   }\n   return NULL;\n}\n\nstatic int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)\n{\n   stbtt_int32 i;\n   stbtt_int32 count = ttUSHORT(fc+nm+2);\n   stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);\n\n   for (i=0; i < count; ++i) {\n      stbtt_uint32 loc = nm + 6 + 12 * i;\n      stbtt_int32 id = ttUSHORT(fc+loc+6);\n      if (id == target_id) {\n         // find the encoding\n         stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);\n\n         // is this a Unicode encoding?\n         if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {\n            stbtt_int32 slen = ttUSHORT(fc+loc+8);\n            stbtt_int32 off = ttUSHORT(fc+loc+10);\n\n            // check if there's a prefix match\n            stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);\n            if (matchlen >= 0) {\n               // check for target_id+1 immediately following, with same encoding & language\n               if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {\n                  slen = ttUSHORT(fc+loc+12+8);\n                  off = ttUSHORT(fc+loc+12+10);\n                  if (slen == 0) {\n                     if (matchlen == nlen)\n                        return 1;\n                  } else if (matchlen < nlen && name[matchlen] == ' ') {\n                     ++matchlen;\n                     if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))\n                        return 1;\n                  }\n               } else {\n                  // if nothing immediately following\n                  if (matchlen == nlen)\n                     return 1;\n               }\n            }\n         }\n\n         // @TODO handle other encodings\n      }\n   }\n   return 0;\n}\n\nstatic int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)\n{\n   stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name);\n   stbtt_uint32 nm,hd;\n   if (!stbtt__isfont(fc+offset)) return 0;\n\n   // check italics/bold/underline flags in macStyle...\n   if (flags) {\n      hd = stbtt__find_table(fc, offset, \"head\");\n      if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;\n   }\n\n   nm = stbtt__find_table(fc, offset, \"name\");\n   if (!nm) return 0;\n\n   if (flags) {\n      // if we checked the macStyle flags, then just check the family and ignore the subfamily\n      if (stbtt__matchpair(fc, nm, name, nlen, 16, -1))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  1, -1))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  3, -1))  return 1;\n   } else {\n      if (stbtt__matchpair(fc, nm, name, nlen, 16, 17))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  1,  2))  return 1;\n      if (stbtt__matchpair(fc, nm, name, nlen,  3, -1))  return 1;\n   }\n\n   return 0;\n}\n\nstatic int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags)\n{\n   stbtt_int32 i;\n   for (i=0;;++i) {\n      stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);\n      if (off < 0) return off;\n      if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))\n         return off;\n   }\n}\n\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wcast-qual\"\n#endif\n\nSTBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,\n                                float pixel_height, unsigned char *pixels, int pw, int ph,\n                                int first_char, int num_chars, stbtt_bakedchar *chardata)\n{\n   return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata);\n}\n\nSTBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index)\n{\n   return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index);\n}\n\nSTBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data)\n{\n   return stbtt_GetNumberOfFonts_internal((unsigned char *) data);\n}\n\nSTBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset)\n{\n   return stbtt_InitFont_internal(info, (unsigned char *) data, offset);\n}\n\nSTBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags)\n{\n   return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags);\n}\n\nSTBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)\n{\n   return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2);\n}\n\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n\n#endif // STB_TRUETYPE_IMPLEMENTATION\n\n\n// FULL VERSION HISTORY\n//\n//   1.25 (2021-07-11) many fixes\n//   1.24 (2020-02-05) fix warning\n//   1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)\n//   1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined\n//   1.21 (2019-02-25) fix warning\n//   1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()\n//   1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod\n//   1.18 (2018-01-29) add missing function\n//   1.17 (2017-07-23) make more arguments const; doc fix\n//   1.16 (2017-07-12) SDF support\n//   1.15 (2017-03-03) make more arguments const\n//   1.14 (2017-01-16) num-fonts-in-TTC function\n//   1.13 (2017-01-02) support OpenType fonts, certain Apple fonts\n//   1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual\n//   1.11 (2016-04-02) fix unused-variable warning\n//   1.10 (2016-04-02) allow user-defined fabs() replacement\n//                     fix memory leak if fontsize=0.0\n//                     fix warning from duplicate typedef\n//   1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges\n//   1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges\n//   1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;\n//                     allow PackFontRanges to pack and render in separate phases;\n//                     fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);\n//                     fixed an assert() bug in the new rasterizer\n//                     replace assert() with STBTT_assert() in new rasterizer\n//   1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)\n//                     also more precise AA rasterizer, except if shapes overlap\n//                     remove need for STBTT_sort\n//   1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC\n//   1.04 (2015-04-15) typo in example\n//   1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes\n//   1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++\n//   1.01 (2014-12-08) fix subpixel position when oversampling to exactly match\n//                        non-oversampled; STBTT_POINT_SIZE for packed case only\n//   1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling\n//   0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)\n//   0.9  (2014-08-07) support certain mac/iOS fonts without an MS platformID\n//   0.8b (2014-07-07) fix a warning\n//   0.8  (2014-05-25) fix a few more warnings\n//   0.7  (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back\n//   0.6c (2012-07-24) improve documentation\n//   0.6b (2012-07-20) fix a few more warnings\n//   0.6  (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,\n//                        stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty\n//   0.5  (2011-12-09) bugfixes:\n//                        subpixel glyph renderer computed wrong bounding box\n//                        first vertex of shape can be off-curve (FreeSans)\n//   0.4b (2011-12-03) fixed an error in the font baking example\n//   0.4  (2011-12-01) kerning, subpixel rendering (tor)\n//                    bugfixes for:\n//                        codepoint-to-glyph conversion using table fmt=12\n//                        codepoint-to-glyph conversion using table fmt=4\n//                        stbtt_GetBakedQuad with non-square texture (Zer)\n//                    updated Hello World! sample to use kerning and subpixel\n//                    fixed some warnings\n//   0.3  (2009-06-24) cmap fmt=12, compound shapes (MM)\n//                    userdata, malloc-from-userdata, non-zero fill (stb)\n//   0.2  (2009-03-11) Fix unsigned/signed char warnings\n//   0.1  (2009-03-09) First public release\n//\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\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------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this\nsoftware, either in source code form or as a compiled binary, for any purpose,\ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this\nsoftware dedicate any and all copyright interest in the software to the public\ndomain. We make this dedication for the benefit of the public at large and to\nthe detriment of our heirs and successors. We intend this dedication to be an\novert act of relinquishment in perpetuity of all present and future rights to\nthis software under copyright law.\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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "third-party/json/LICENSE.MIT",
    "content": "MIT License \n\nCopyright (c) 2013-2022 Niels Lohmann\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": "third-party/json/json.hpp",
    "content": "//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n/****************************************************************************\\\n * Note on documentation: The source files contain links to the online      *\n * documentation of the public API at https://json.nlohmann.me. This URL    *\n * contains the most recent documentation and should also be applicable to  *\n * previous versions; documentation for deprecated functions is not         *\n * removed, but marked deprecated. See \"Generate documentation\" section in  *\n * file docs/README.md.                                                     *\n\\****************************************************************************/\n\n#ifndef INCLUDE_NLOHMANN_JSON_HPP_\n#define INCLUDE_NLOHMANN_JSON_HPP_\n\n#include <algorithm> // all_of, find, for_each\n#include <cstddef> // nullptr_t, ptrdiff_t, size_t\n#include <functional> // hash, less\n#include <initializer_list> // initializer_list\n#ifndef JSON_NO_IO\n    #include <iosfwd> // istream, ostream\n#endif  // JSON_NO_IO\n#include <iterator> // random_access_iterator_tag\n#include <memory> // unique_ptr\n#include <numeric> // accumulate\n#include <string> // string, stoi, to_string\n#include <utility> // declval, forward, move, pair, swap\n#include <vector> // vector\n\n// #include <nlohmann/adl_serializer.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <utility>\n\n// #include <nlohmann/detail/abi_macros.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n// This file contains all macro definitions affecting or depending on the ABI\n\n#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK\n    #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)\n        #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 2\n            #warning \"Already included a different version of the library!\"\n        #endif\n    #endif\n#endif\n\n#define NLOHMANN_JSON_VERSION_MAJOR 3   // NOLINT(modernize-macro-to-enum)\n#define NLOHMANN_JSON_VERSION_MINOR 11  // NOLINT(modernize-macro-to-enum)\n#define NLOHMANN_JSON_VERSION_PATCH 2   // NOLINT(modernize-macro-to-enum)\n\n#ifndef JSON_DIAGNOSTICS\n    #define JSON_DIAGNOSTICS 0\n#endif\n\n#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON\n    #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0\n#endif\n\n#if JSON_DIAGNOSTICS\n    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag\n#else\n    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS\n#endif\n\n#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON\n    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp\n#else\n    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON\n#endif\n\n#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION\n    #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0\n#endif\n\n// Construct the namespace ABI tags component\n#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b\n#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \\\n    NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)\n\n#define NLOHMANN_JSON_ABI_TAGS                                       \\\n    NLOHMANN_JSON_ABI_TAGS_CONCAT(                                   \\\n            NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS,                       \\\n            NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)\n\n// Construct the namespace version component\n#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \\\n    _v ## major ## _ ## minor ## _ ## patch\n#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \\\n    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)\n\n#if NLOHMANN_JSON_NAMESPACE_NO_VERSION\n#define NLOHMANN_JSON_NAMESPACE_VERSION\n#else\n#define NLOHMANN_JSON_NAMESPACE_VERSION                                 \\\n    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \\\n                                           NLOHMANN_JSON_VERSION_MINOR, \\\n                                           NLOHMANN_JSON_VERSION_PATCH)\n#endif\n\n// Combine namespace components\n#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b\n#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \\\n    NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)\n\n#ifndef NLOHMANN_JSON_NAMESPACE\n#define NLOHMANN_JSON_NAMESPACE               \\\n    nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \\\n            NLOHMANN_JSON_ABI_TAGS,           \\\n            NLOHMANN_JSON_NAMESPACE_VERSION)\n#endif\n\n#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN\n#define NLOHMANN_JSON_NAMESPACE_BEGIN                \\\n    namespace nlohmann                               \\\n    {                                                \\\n    inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \\\n                NLOHMANN_JSON_ABI_TAGS,              \\\n                NLOHMANN_JSON_NAMESPACE_VERSION)     \\\n    {\n#endif\n\n#ifndef NLOHMANN_JSON_NAMESPACE_END\n#define NLOHMANN_JSON_NAMESPACE_END                                     \\\n    }  /* namespace (inline namespace) NOLINT(readability/namespace) */ \\\n    }  // namespace nlohmann\n#endif\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <algorithm> // transform\n#include <array> // array\n#include <forward_list> // forward_list\n#include <iterator> // inserter, front_inserter, end\n#include <map> // map\n#include <string> // string\n#include <tuple> // tuple, make_tuple\n#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible\n#include <unordered_map> // unordered_map\n#include <utility> // pair, declval\n#include <valarray> // valarray\n\n// #include <nlohmann/detail/exceptions.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <cstddef> // nullptr_t\n#include <exception> // exception\n#include <stdexcept> // runtime_error\n#include <string> // to_string\n#include <vector> // vector\n\n// #include <nlohmann/detail/value_t.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t\n#include <string> // string\n\n// #include <nlohmann/detail/macro_scope.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <utility> // declval, pair\n// #include <nlohmann/detail/meta/detected.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <type_traits>\n\n// #include <nlohmann/detail/meta/void_t.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename ...Ts> struct make_void\n{\n    using type = void;\n};\ntemplate<typename ...Ts> using void_t = typename make_void<Ts...>::type;\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n// https://en.cppreference.com/w/cpp/experimental/is_detected\nstruct nonesuch\n{\n    nonesuch() = delete;\n    ~nonesuch() = delete;\n    nonesuch(nonesuch const&) = delete;\n    nonesuch(nonesuch const&&) = delete;\n    void operator=(nonesuch const&) = delete;\n    void operator=(nonesuch&&) = delete;\n};\n\ntemplate<class Default,\n         class AlwaysVoid,\n         template<class...> class Op,\n         class... Args>\nstruct detector\n{\n    using value_t = std::false_type;\n    using type = Default;\n};\n\ntemplate<class Default, template<class...> class Op, class... Args>\nstruct detector<Default, void_t<Op<Args...>>, Op, Args...>\n{\n    using value_t = std::true_type;\n    using type = Op<Args...>;\n};\n\ntemplate<template<class...> class Op, class... Args>\nusing is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;\n\ntemplate<template<class...> class Op, class... Args>\nstruct is_detected_lazy : is_detected<Op, Args...> { };\n\ntemplate<template<class...> class Op, class... Args>\nusing detected_t = typename detector<nonesuch, void, Op, Args...>::type;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or = detector<Default, void, Op, Args...>;\n\ntemplate<class Default, template<class...> class Op, class... Args>\nusing detected_or_t = typename detected_or<Default, Op, Args...>::type;\n\ntemplate<class Expected, template<class...> class Op, class... Args>\nusing is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;\n\ntemplate<class To, template<class...> class Op, class... Args>\nusing is_detected_convertible =\n    std::is_convertible<detected_t<Op, Args...>, To>;\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/thirdparty/hedley/hedley.hpp>\n\n\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson <evan@nemerson.com>\n// SPDX-License-Identifier: MIT\n\n/* Hedley - https://nemequ.github.io/hedley\n * Created by Evan Nemerson <evan@nemerson.com>\n */\n\n#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15)\n#if defined(JSON_HEDLEY_VERSION)\n    #undef JSON_HEDLEY_VERSION\n#endif\n#define JSON_HEDLEY_VERSION 15\n\n#if defined(JSON_HEDLEY_STRINGIFY_EX)\n    #undef JSON_HEDLEY_STRINGIFY_EX\n#endif\n#define JSON_HEDLEY_STRINGIFY_EX(x) #x\n\n#if defined(JSON_HEDLEY_STRINGIFY)\n    #undef JSON_HEDLEY_STRINGIFY\n#endif\n#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)\n\n#if defined(JSON_HEDLEY_CONCAT_EX)\n    #undef JSON_HEDLEY_CONCAT_EX\n#endif\n#define JSON_HEDLEY_CONCAT_EX(a,b) a##b\n\n#if defined(JSON_HEDLEY_CONCAT)\n    #undef JSON_HEDLEY_CONCAT\n#endif\n#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)\n\n#if defined(JSON_HEDLEY_CONCAT3_EX)\n    #undef JSON_HEDLEY_CONCAT3_EX\n#endif\n#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c\n\n#if defined(JSON_HEDLEY_CONCAT3)\n    #undef JSON_HEDLEY_CONCAT3\n#endif\n#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c)\n\n#if defined(JSON_HEDLEY_VERSION_ENCODE)\n    #undef JSON_HEDLEY_VERSION_ENCODE\n#endif\n#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)\n    #undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)\n\n#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)\n    #undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#endif\n#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)\n\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #undef JSON_HEDLEY_GNUC_VERSION\n#endif\n#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)\n#elif defined(__GNUC__)\n    #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GNUC_VERSION)\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION)\n    #undef JSON_HEDLEY_MSVC_VERSION\n#endif\n#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)\n#elif defined(_MSC_FULL_VER) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)\n#elif defined(_MSC_VER) && !defined(__ICL)\n    #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)\n    #undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#endif\n#if !defined(JSON_HEDLEY_MSVC_VERSION)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)\n#elif defined(_MSC_VER) && (_MSC_VER >= 1400)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))\n#elif defined(_MSC_VER) && (_MSC_VER >= 1200)\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))\n#else\n    #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #undef JSON_HEDLEY_INTEL_VERSION\n#endif\n#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)\n#elif defined(__INTEL_COMPILER) && !defined(__ICL)\n    #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)\n    #undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_INTEL_VERSION)\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION)\n    #undef JSON_HEDLEY_INTEL_CL_VERSION\n#endif\n#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL)\n    #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0)\n#endif\n\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK)\n    #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_INTEL_CL_VERSION)\n    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #undef JSON_HEDLEY_PGI_VERSION\n#endif\n#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)\n    #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)\n#endif\n\n#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)\n    #undef JSON_HEDLEY_PGI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PGI_VERSION)\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #undef JSON_HEDLEY_SUNPRO_VERSION\n#endif\n#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)\n#elif defined(__SUNPRO_C)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)\n#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)\n#elif defined(__SUNPRO_CC)\n    #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)\n    #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_SUNPRO_VERSION)\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#endif\n#if defined(__EMSCRIPTEN__)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)\n#endif\n\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)\n    #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #undef JSON_HEDLEY_ARM_VERSION\n#endif\n#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)\n#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)\n#endif\n\n#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)\n    #undef JSON_HEDLEY_ARM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_ARM_VERSION)\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #undef JSON_HEDLEY_IBM_VERSION\n#endif\n#if defined(__ibmxl__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)\n#elif defined(__xlC__) && defined(__xlC_ver__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)\n#elif defined(__xlC__)\n    #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)\n#endif\n\n#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)\n    #undef JSON_HEDLEY_IBM_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IBM_VERSION)\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #undef JSON_HEDLEY_TI_VERSION\n#endif\n#if \\\n    defined(__TI_COMPILER_VERSION__) && \\\n    ( \\\n      defined(__TMS470__) || defined(__TI_ARM__) || \\\n      defined(__MSP430__) || \\\n      defined(__TMS320C2000__) \\\n    )\n#if (__TI_COMPILER_VERSION__ >= 16000000)\n    #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n#endif\n\n#if defined(JSON_HEDLEY_TI_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_VERSION)\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__)\n    #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL2000_VERSION)\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #undef JSON_HEDLEY_TI_CL430_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__)\n    #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL430_VERSION)\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__))\n    #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__)\n    #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL6X_VERSION)\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__)\n    #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CL7X_VERSION)\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION\n#endif\n#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))\n#endif\n\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK)\n    #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #undef JSON_HEDLEY_CRAY_VERSION\n#endif\n#if defined(_CRAYC)\n    #if defined(_RELEASE_PATCHLEVEL)\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)\n    #else\n        #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)\n    #undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_CRAY_VERSION)\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #undef JSON_HEDLEY_IAR_VERSION\n#endif\n#if defined(__IAR_SYSTEMS_ICC__)\n    #if __VER__ > 1000\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))\n    #else\n        #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0)\n    #endif\n#endif\n\n#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)\n    #undef JSON_HEDLEY_IAR_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_IAR_VERSION)\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #undef JSON_HEDLEY_TINYC_VERSION\n#endif\n#if defined(__TINYC__)\n    #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)\n    #undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #undef JSON_HEDLEY_DMC_VERSION\n#endif\n#if defined(__DMC__)\n    #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)\n#endif\n\n#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)\n    #undef JSON_HEDLEY_DMC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_DMC_VERSION)\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #undef JSON_HEDLEY_COMPCERT_VERSION\n#endif\n#if defined(__COMPCERT_VERSION__)\n    #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)\n#endif\n\n#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)\n    #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_COMPCERT_VERSION)\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #undef JSON_HEDLEY_PELLES_VERSION\n#endif\n#if defined(__POCC__)\n    #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)\n#endif\n\n#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)\n    #undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_PELLES_VERSION)\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #undef JSON_HEDLEY_MCST_LCC_VERSION\n#endif\n#if defined(__LCC__) && defined(__LCC_MINOR__)\n    #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__)\n#endif\n\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK)\n    #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #undef JSON_HEDLEY_GCC_VERSION\n#endif\n#if \\\n    defined(JSON_HEDLEY_GNUC_VERSION) && \\\n    !defined(__clang__) && \\\n    !defined(JSON_HEDLEY_INTEL_VERSION) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_ARM_VERSION) && \\\n    !defined(JSON_HEDLEY_CRAY_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL430_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \\\n    !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \\\n    !defined(__COMPCERT__) && \\\n    !defined(JSON_HEDLEY_MCST_LCC_VERSION)\n    #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION\n#endif\n\n#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_VERSION_CHECK\n#endif\n#if defined(JSON_HEDLEY_GCC_VERSION)\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))\n#else\n    #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_ATTRIBUTE\n#endif\n#if \\\n  defined(__has_attribute) && \\\n  ( \\\n    (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \\\n  )\n#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)\n#else\n#  define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#endif\n#if defined(__has_attribute)\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#endif\n#if \\\n    defined(__has_cpp_attribute) && \\\n    defined(__cplusplus) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)\n    #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#endif\n#if !defined(__cplusplus) || !defined(__has_cpp_attribute)\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#elif \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_IAR_VERSION) && \\\n    (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \\\n    (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)\n#else\n    #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#endif\n#if defined(__has_cpp_attribute) && defined(__cplusplus)\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_BUILTIN)\n    #undef JSON_HEDLEY_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)\n    #undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#endif\n#if defined(__has_builtin)\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)\n#else\n    #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_FEATURE)\n    #undef JSON_HEDLEY_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_HAS_FEATURE(feature) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)\n    #undef JSON_HEDLEY_GCC_HAS_FEATURE\n#endif\n#if defined(__has_feature)\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)\n#else\n    #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_EXTENSION)\n    #undef JSON_HEDLEY_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)\n    #undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#endif\n#if defined(__has_extension)\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)\n#else\n    #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#endif\n#if defined(__has_declspec_attribute)\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)\n#else\n    #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_HAS_WARNING)\n    #undef JSON_HEDLEY_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_HAS_WARNING(warning) (0)\n#endif\n\n#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)\n    #undef JSON_HEDLEY_GNUC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_GCC_HAS_WARNING)\n    #undef JSON_HEDLEY_GCC_HAS_WARNING\n#endif\n#if defined(__has_warning)\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)\n#else\n    #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    defined(__clang__) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \\\n    (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))\n    #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_PRAGMA(value) __pragma(value)\n#else\n    #define JSON_HEDLEY_PRAGMA(value)\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)\n    #undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#endif\n#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)\n    #undef JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"clang diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"clang diagnostic pop\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"GCC diagnostic push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"GCC diagnostic pop\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))\n    #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))\n#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"pop\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"diag_push\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"diag_pop\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma(\"warning(push)\")\n    #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma(\"warning(pop)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_PUSH\n    #define JSON_HEDLEY_DIAGNOSTIC_POP\n#endif\n\n/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat\")\n#    if JSON_HEDLEY_HAS_WARNING(\"-Wc++17-extensions\")\n#      if JSON_HEDLEY_HAS_WARNING(\"-Wc++1z-extensions\")\n#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++17-extensions\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++1z-extensions\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#      else\n#        define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++17-extensions\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#      endif\n#    else\n#      define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wc++98-compat\\\"\") \\\n    xpr \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    endif\n#  endif\n#endif\n#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x\n#endif\n\n#if defined(JSON_HEDLEY_CONST_CAST)\n    #undef JSON_HEDLEY_CONST_CAST\n#endif\n#if defined(__cplusplus)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))\n#elif \\\n  JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\") || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_REINTERPRET_CAST)\n    #undef JSON_HEDLEY_REINTERPRET_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_CAST)\n    #undef JSON_HEDLEY_STATIC_CAST\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))\n#else\n    #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))\n#endif\n\n#if defined(JSON_HEDLEY_CPP_CAST)\n    #undef JSON_HEDLEY_CPP_CAST\n#endif\n#if defined(__cplusplus)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wold-style-cast\")\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wold-style-cast\\\"\") \\\n    ((T) (expr)) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0)\n#    define JSON_HEDLEY_CPP_CAST(T, expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"diag_suppress=Pe137\") \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  else\n#    define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))\n#  endif\n#else\n#  define JSON_HEDLEY_CPP_CAST(T, expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wdeprecated-declarations\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"clang diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warning(disable:1478 1786)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1216,1444,1445\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1444\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1215,1444\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress 1291,1718\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"error_messages(off,symdeprecated,symdeprecated2)\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"diag_suppress=Pe1444,Pe1215\")\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma(\"warn(disable:2241)\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"clang diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"warning(disable:161)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 1675\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"GCC diagnostic ignored \\\"-Wunknown-pragmas\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 163\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress=Pe161\")\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma(\"diag_suppress 161\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-attributes\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"clang diagnostic ignored \\\"-Wunknown-attributes\\\"\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"GCC diagnostic ignored \\\"-Wdeprecated-declarations\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"warning(disable:1292)\")\n#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292))\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097,1098\")\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097\")\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"error_messages(off,attrskipunsup)\")\n#elif \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1173\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress=Pe1097\")\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma(\"diag_suppress 1097\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wcast-qual\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"clang diagnostic ignored \\\"-Wcast-qual\\\"\")\n#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"warning(disable:2203 2331)\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma(\"GCC diagnostic ignored \\\"-Wcast-qual\\\"\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#endif\n\n#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION)\n    #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunused-function\")\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"clang diagnostic ignored \\\"-Wunused-function\\\"\")\n#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"GCC diagnostic ignored \\\"-Wunused-function\\\"\")\n#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505))\n#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma(\"diag_suppress 3142\")\n#else\n    #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION\n#endif\n\n#if defined(JSON_HEDLEY_DEPRECATED)\n    #undef JSON_HEDLEY_DEPRECATED\n#endif\n#if defined(JSON_HEDLEY_DEPRECATED_FOR)\n    #undef JSON_HEDLEY_DEPRECATED_FOR\n#endif\n#if \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated(\"Since \" # since))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated(\"Since \" #since \"; use \" #replacement))\n#elif \\\n    (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__(\"Since \" #since)))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__(\"Since \" #since \"; use \" #replacement)))\n#elif defined(__cplusplus) && (__cplusplus >= 201402L)\n    #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since)]])\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated(\"Since \" #since \"; use \" #replacement)]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_DEPRECATED(since) _Pragma(\"deprecated\")\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma(\"deprecated\")\n#else\n    #define JSON_HEDLEY_DEPRECATED(since)\n    #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)\n#endif\n\n#if defined(JSON_HEDLEY_UNAVAILABLE)\n    #undef JSON_HEDLEY_UNAVAILABLE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__(\"Not available until \" #available_since)))\n#else\n    #define JSON_HEDLEY_UNAVAILABLE(available_since)\n#endif\n\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#endif\n#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)\n    #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))\n#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])\n#elif defined(_Check_return_) /* SAL */\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_\n#else\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT\n    #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg)\n#endif\n\n#if defined(JSON_HEDLEY_SENTINEL)\n    #undef JSON_HEDLEY_SENTINEL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))\n#else\n    #define JSON_HEDLEY_SENTINEL(position)\n#endif\n\n#if defined(JSON_HEDLEY_NO_RETURN)\n    #undef JSON_HEDLEY_NO_RETURN\n#endif\n#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NO_RETURN __noreturn\n#elif \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L\n    #define JSON_HEDLEY_NO_RETURN _Noreturn\n#elif defined(__cplusplus) && (__cplusplus >= 201103L)\n    #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"does_not_return\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NO_RETURN _Pragma(\"FUNC_NEVER_RETURNS;\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)\n#else\n    #define JSON_HEDLEY_NO_RETURN\n#endif\n\n#if defined(JSON_HEDLEY_NO_ESCAPE)\n    #undef JSON_HEDLEY_NO_ESCAPE\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)\n    #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))\n#else\n    #define JSON_HEDLEY_NO_ESCAPE\n#endif\n\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #undef JSON_HEDLEY_UNREACHABLE\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)\n    #undef JSON_HEDLEY_UNREACHABLE_RETURN\n#endif\n#if defined(JSON_HEDLEY_ASSUME)\n    #undef JSON_HEDLEY_ASSUME\n#endif\n#if \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_ASSUME(expr) __assume(expr)\n#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)\n    #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)\n#elif \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n    #if defined(__cplusplus)\n        #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)\n    #endif\n#endif\n#if \\\n    (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()\n#elif defined(JSON_HEDLEY_ASSUME)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n#if !defined(JSON_HEDLEY_ASSUME)\n    #if defined(JSON_HEDLEY_UNREACHABLE)\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1)))\n    #else\n        #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr)\n    #endif\n#endif\n#if defined(JSON_HEDLEY_UNREACHABLE)\n    #if  \\\n        JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value))\n    #else\n        #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()\n    #endif\n#else\n    #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value)\n#endif\n#if !defined(JSON_HEDLEY_UNREACHABLE)\n    #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)\n#endif\n\nJSON_HEDLEY_DIAGNOSTIC_PUSH\n#if JSON_HEDLEY_HAS_WARNING(\"-Wpedantic\")\n    #pragma clang diagnostic ignored \"-Wpedantic\"\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wc++98-compat-pedantic\") && defined(__cplusplus)\n    #pragma clang diagnostic ignored \"-Wc++98-compat-pedantic\"\n#endif\n#if JSON_HEDLEY_GCC_HAS_WARNING(\"-Wvariadic-macros\",4,0,0)\n    #if defined(__clang__)\n        #pragma clang diagnostic ignored \"-Wvariadic-macros\"\n    #elif defined(JSON_HEDLEY_GCC_VERSION)\n        #pragma GCC diagnostic ignored \"-Wvariadic-macros\"\n    #endif\n#endif\n#if defined(JSON_HEDLEY_NON_NULL)\n    #undef JSON_HEDLEY_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))\n#else\n    #define JSON_HEDLEY_NON_NULL(...)\n#endif\nJSON_HEDLEY_DIAGNOSTIC_POP\n\n#if defined(JSON_HEDLEY_PRINTF_FORMAT)\n    #undef JSON_HEDLEY_PRINTF_FORMAT\n#endif\n#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))\n#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))\n#elif \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(format) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))\n#else\n    #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)\n#endif\n\n#if defined(JSON_HEDLEY_CONSTEXPR)\n    #undef JSON_HEDLEY_CONSTEXPR\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)\n    #endif\n#endif\n#if !defined(JSON_HEDLEY_CONSTEXPR)\n    #define JSON_HEDLEY_CONSTEXPR\n#endif\n\n#if defined(JSON_HEDLEY_PREDICT)\n    #undef JSON_HEDLEY_PREDICT\n#endif\n#if defined(JSON_HEDLEY_LIKELY)\n    #undef JSON_HEDLEY_LIKELY\n#endif\n#if defined(JSON_HEDLEY_UNLIKELY)\n    #undef JSON_HEDLEY_UNLIKELY\n#endif\n#if defined(JSON_HEDLEY_UNPREDICTABLE)\n    #undef JSON_HEDLEY_UNPREDICTABLE\n#endif\n#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))\n#endif\n#if \\\n  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(  (expr), (value), (probability))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability)   __builtin_expect_with_probability(!!(expr),    1   , (probability))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability)  __builtin_expect_with_probability(!!(expr),    0   , (probability))\n#  define JSON_HEDLEY_LIKELY(expr)                      __builtin_expect                 (!!(expr),    1                  )\n#  define JSON_HEDLEY_UNLIKELY(expr)                    __builtin_expect                 (!!(expr),    0                  )\n#elif \\\n  (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \\\n  JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) \\\n    (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \\\n    (__extension__ ({ \\\n        double hedley_probability_ = (probability); \\\n        ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \\\n    }))\n#  define JSON_HEDLEY_LIKELY(expr)   __builtin_expect(!!(expr), 1)\n#  define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)\n#else\n#  define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))\n#  define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))\n#  define JSON_HEDLEY_LIKELY(expr) (!!(expr))\n#  define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))\n#endif\n#if !defined(JSON_HEDLEY_UNPREDICTABLE)\n    #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)\n#endif\n\n#if defined(JSON_HEDLEY_MALLOC)\n    #undef JSON_HEDLEY_MALLOC\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_MALLOC _Pragma(\"returns_new_memory\")\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_MALLOC __declspec(restrict)\n#else\n    #define JSON_HEDLEY_MALLOC\n#endif\n\n#if defined(JSON_HEDLEY_PURE)\n    #undef JSON_HEDLEY_PURE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#  define JSON_HEDLEY_PURE __attribute__((__pure__))\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n#  define JSON_HEDLEY_PURE _Pragma(\"does_not_write_global_data\")\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \\\n    )\n#  define JSON_HEDLEY_PURE _Pragma(\"FUNC_IS_PURE;\")\n#else\n#  define JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_CONST)\n    #undef JSON_HEDLEY_CONST\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(const) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_CONST __attribute__((__const__))\n#elif \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)\n    #define JSON_HEDLEY_CONST _Pragma(\"no_side_effect\")\n#else\n    #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE\n#endif\n\n#if defined(JSON_HEDLEY_RESTRICT)\n    #undef JSON_HEDLEY_RESTRICT\n#endif\n#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT restrict\n#elif \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \\\n    defined(__clang__) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_RESTRICT __restrict\n#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)\n    #define JSON_HEDLEY_RESTRICT _Restrict\n#else\n    #define JSON_HEDLEY_RESTRICT\n#endif\n\n#if defined(JSON_HEDLEY_INLINE)\n    #undef JSON_HEDLEY_INLINE\n#endif\n#if \\\n    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \\\n    (defined(__cplusplus) && (__cplusplus >= 199711L))\n    #define JSON_HEDLEY_INLINE inline\n#elif \\\n    defined(JSON_HEDLEY_GCC_VERSION) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)\n    #define JSON_HEDLEY_INLINE __inline__\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_INLINE __inline\n#else\n    #define JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_ALWAYS_INLINE)\n    #undef JSON_HEDLEY_ALWAYS_INLINE\n#endif\n#if \\\n  JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n  JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n  JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n  JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n  JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n  (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n  (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n  (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n  (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n  JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n  JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n  JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n  JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n  JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE\n#elif \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE __forceinline\n#elif defined(__cplusplus) && \\\n    ( \\\n      JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n      JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n      JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n      JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n      JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n      JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \\\n    )\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"FUNC_ALWAYS_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_ALWAYS_INLINE _Pragma(\"inline=forced\")\n#else\n#  define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_NEVER_INLINE)\n    #undef JSON_HEDLEY_NEVER_INLINE\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \\\n    JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \\\n    (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \\\n    (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \\\n    (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \\\n    (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \\\n    JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \\\n    JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \\\n    JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"noinline\")\n#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"FUNC_CANNOT_INLINE;\")\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE _Pragma(\"inline=never\")\n#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)\n    #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)\n    #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)\n#else\n    #define JSON_HEDLEY_NEVER_INLINE\n#endif\n\n#if defined(JSON_HEDLEY_PRIVATE)\n    #undef JSON_HEDLEY_PRIVATE\n#endif\n#if defined(JSON_HEDLEY_PUBLIC)\n    #undef JSON_HEDLEY_PUBLIC\n#endif\n#if defined(JSON_HEDLEY_IMPORT)\n    #undef JSON_HEDLEY_IMPORT\n#endif\n#if defined(_WIN32) || defined(__CYGWIN__)\n#  define JSON_HEDLEY_PRIVATE\n#  define JSON_HEDLEY_PUBLIC   __declspec(dllexport)\n#  define JSON_HEDLEY_IMPORT   __declspec(dllimport)\n#else\n#  if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    ( \\\n      defined(__TI_EABI__) && \\\n      ( \\\n        (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \\\n        JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \\\n      ) \\\n    ) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n#    define JSON_HEDLEY_PRIVATE __attribute__((__visibility__(\"hidden\")))\n#    define JSON_HEDLEY_PUBLIC  __attribute__((__visibility__(\"default\")))\n#  else\n#    define JSON_HEDLEY_PRIVATE\n#    define JSON_HEDLEY_PUBLIC\n#  endif\n#  define JSON_HEDLEY_IMPORT    extern\n#endif\n\n#if defined(JSON_HEDLEY_NO_THROW)\n    #undef JSON_HEDLEY_NO_THROW\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))\n#elif \\\n    JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)\n    #define JSON_HEDLEY_NO_THROW __declspec(nothrow)\n#else\n    #define JSON_HEDLEY_NO_THROW\n#endif\n\n#if defined(JSON_HEDLEY_FALL_THROUGH)\n    #undef JSON_HEDLEY_FALL_THROUGH\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])\n#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)\n    #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])\n#elif defined(__fallthrough) /* SAL */\n    #define JSON_HEDLEY_FALL_THROUGH __fallthrough\n#else\n    #define JSON_HEDLEY_FALL_THROUGH\n#endif\n\n#if defined(JSON_HEDLEY_RETURNS_NON_NULL)\n    #undef JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n#if \\\n    JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))\n#elif defined(_Ret_notnull_) /* SAL */\n    #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_\n#else\n    #define JSON_HEDLEY_RETURNS_NON_NULL\n#endif\n\n#if defined(JSON_HEDLEY_ARRAY_PARAM)\n    #undef JSON_HEDLEY_ARRAY_PARAM\n#endif\n#if \\\n    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \\\n    !defined(__STDC_NO_VLA__) && \\\n    !defined(__cplusplus) && \\\n    !defined(JSON_HEDLEY_PGI_VERSION) && \\\n    !defined(JSON_HEDLEY_TINYC_VERSION)\n    #define JSON_HEDLEY_ARRAY_PARAM(name) (name)\n#else\n    #define JSON_HEDLEY_ARRAY_PARAM(name)\n#endif\n\n#if defined(JSON_HEDLEY_IS_CONSTANT)\n    #undef JSON_HEDLEY_IS_CONSTANT\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)\n    #undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#endif\n/* JSON_HEDLEY_IS_CONSTEXPR_ is for\n   HEDLEY INTERNAL USE ONLY.  API subject to change without notice. */\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #undef JSON_HEDLEY_IS_CONSTEXPR_\n#endif\n#if \\\n    JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \\\n    JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n    JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n    JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \\\n    JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \\\n    JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n    JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \\\n    (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \\\n    JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n    JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)\n    #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)\n#endif\n#if !defined(__cplusplus)\n#  if \\\n       JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \\\n       JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \\\n       JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)\n#endif\n#  elif \\\n       ( \\\n          defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \\\n          !defined(JSON_HEDLEY_SUNPRO_VERSION) && \\\n          !defined(JSON_HEDLEY_PGI_VERSION) && \\\n          !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n       (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \\\n       JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \\\n       JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \\\n       JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \\\n       JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)\n#if defined(__INTPTR_TYPE__)\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)\n#else\n    #include <stdint.h>\n    #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)\n#endif\n#  elif \\\n       defined(JSON_HEDLEY_GCC_VERSION) || \\\n       defined(JSON_HEDLEY_INTEL_VERSION) || \\\n       defined(JSON_HEDLEY_TINYC_VERSION) || \\\n       defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \\\n       JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \\\n       defined(JSON_HEDLEY_TI_CL2000_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL6X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CL7X_VERSION) || \\\n       defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \\\n       defined(__clang__)\n#    define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \\\n        sizeof(void) != \\\n        sizeof(*( \\\n                  1 ? \\\n                  ((void*) ((expr) * 0L) ) : \\\n((struct { char v[sizeof(void) * 2]; } *) 1) \\\n                ) \\\n              ) \\\n                                            )\n#  endif\n#endif\n#if defined(JSON_HEDLEY_IS_CONSTEXPR_)\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))\n#else\n    #if !defined(JSON_HEDLEY_IS_CONSTANT)\n        #define JSON_HEDLEY_IS_CONSTANT(expr) (0)\n    #endif\n    #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)\n#endif\n\n#if defined(JSON_HEDLEY_BEGIN_C_DECLS)\n    #undef JSON_HEDLEY_BEGIN_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_END_C_DECLS)\n    #undef JSON_HEDLEY_END_C_DECLS\n#endif\n#if defined(JSON_HEDLEY_C_DECL)\n    #undef JSON_HEDLEY_C_DECL\n#endif\n#if defined(__cplusplus)\n    #define JSON_HEDLEY_BEGIN_C_DECLS extern \"C\" {\n    #define JSON_HEDLEY_END_C_DECLS }\n    #define JSON_HEDLEY_C_DECL extern \"C\"\n#else\n    #define JSON_HEDLEY_BEGIN_C_DECLS\n    #define JSON_HEDLEY_END_C_DECLS\n    #define JSON_HEDLEY_C_DECL\n#endif\n\n#if defined(JSON_HEDLEY_STATIC_ASSERT)\n    #undef JSON_HEDLEY_STATIC_ASSERT\n#endif\n#if \\\n  !defined(__cplusplus) && ( \\\n      (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \\\n      (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \\\n      JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \\\n      JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \\\n      defined(_Static_assert) \\\n    )\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)\n#elif \\\n  (defined(__cplusplus) && (__cplusplus >= 201103L)) || \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))\n#else\n#  define JSON_HEDLEY_STATIC_ASSERT(expr, message)\n#endif\n\n#if defined(JSON_HEDLEY_NULL)\n    #undef JSON_HEDLEY_NULL\n#endif\n#if defined(__cplusplus)\n    #if __cplusplus >= 201103L\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)\n    #elif defined(NULL)\n        #define JSON_HEDLEY_NULL NULL\n    #else\n        #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)\n    #endif\n#elif defined(NULL)\n    #define JSON_HEDLEY_NULL NULL\n#else\n    #define JSON_HEDLEY_NULL ((void*) 0)\n#endif\n\n#if defined(JSON_HEDLEY_MESSAGE)\n    #undef JSON_HEDLEY_MESSAGE\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_MESSAGE(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(message msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)\n#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)\n#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)\n#  define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_WARNING)\n    #undef JSON_HEDLEY_WARNING\n#endif\n#if JSON_HEDLEY_HAS_WARNING(\"-Wunknown-pragmas\")\n#  define JSON_HEDLEY_WARNING(msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \\\n    JSON_HEDLEY_PRAGMA(clang warning msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#elif \\\n  JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \\\n  JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \\\n  JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)\n#elif \\\n  JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \\\n  JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))\n#else\n#  define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)\n#endif\n\n#if defined(JSON_HEDLEY_REQUIRE)\n    #undef JSON_HEDLEY_REQUIRE\n#endif\n#if defined(JSON_HEDLEY_REQUIRE_MSG)\n    #undef JSON_HEDLEY_REQUIRE_MSG\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)\n#  if JSON_HEDLEY_HAS_WARNING(\"-Wgcc-compat\")\n#    define JSON_HEDLEY_REQUIRE(expr) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), #expr, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \\\n    JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n    _Pragma(\"clang diagnostic ignored \\\"-Wgcc-compat\\\"\") \\\n    __attribute__((diagnose_if(!(expr), msg, \"error\"))) \\\n    JSON_HEDLEY_DIAGNOSTIC_POP\n#  else\n#    define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, \"error\")))\n#    define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, \"error\")))\n#  endif\n#else\n#  define JSON_HEDLEY_REQUIRE(expr)\n#  define JSON_HEDLEY_REQUIRE_MSG(expr,msg)\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS)\n    #undef JSON_HEDLEY_FLAGS\n#endif\n#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING(\"-Wbitfield-enum-conversion\"))\n    #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))\n#else\n    #define JSON_HEDLEY_FLAGS\n#endif\n\n#if defined(JSON_HEDLEY_FLAGS_CAST)\n    #undef JSON_HEDLEY_FLAGS_CAST\n#endif\n#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \\\n        JSON_HEDLEY_DIAGNOSTIC_PUSH \\\n        _Pragma(\"warning(disable:188)\") \\\n        ((T) (expr)); \\\n        JSON_HEDLEY_DIAGNOSTIC_POP \\\n    }))\n#else\n#  define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)\n#endif\n\n#if defined(JSON_HEDLEY_EMPTY_BASES)\n    #undef JSON_HEDLEY_EMPTY_BASES\n#endif\n#if \\\n    (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \\\n    JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)\n    #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)\n#else\n    #define JSON_HEDLEY_EMPTY_BASES\n#endif\n\n/* Remaining macros are deprecated. */\n\n#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)\n    #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#endif\n#if defined(__clang__)\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)\n#else\n    #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)\n#endif\n\n#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)\n    #undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#endif\n#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)\n    #undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)\n    #undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#endif\n#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)\n    #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#endif\n#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)\n\n#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)\n    #undef JSON_HEDLEY_CLANG_HAS_WARNING\n#endif\n#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)\n\n#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */\n\n\n// This file contains all internal macro definitions (except those affecting ABI)\n// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n\n// exclude unsupported compilers\n#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)\n    #if defined(__clang__)\n        #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400\n            #error \"unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))\n        #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800\n            #error \"unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #endif\n#endif\n\n// C++ language standard detection\n// if the user manually specified the used c++ version this is skipped\n#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)\n    #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)\n        #define JSON_HAS_CPP_20\n        #define JSON_HAS_CPP_17\n        #define JSON_HAS_CPP_14\n    #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464\n        #define JSON_HAS_CPP_17\n        #define JSON_HAS_CPP_14\n    #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)\n        #define JSON_HAS_CPP_14\n    #endif\n    // the cpp 11 flag is always specified because it is the minimal required version\n    #define JSON_HAS_CPP_11\n#endif\n\n#ifdef __has_include\n    #if __has_include(<version>)\n        #include <version>\n    #endif\n#endif\n\n#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)\n    #ifdef JSON_HAS_CPP_17\n        #if defined(__cpp_lib_filesystem)\n            #define JSON_HAS_FILESYSTEM 1\n        #elif defined(__cpp_lib_experimental_filesystem)\n            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1\n        #elif !defined(__has_include)\n            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1\n        #elif __has_include(<filesystem>)\n            #define JSON_HAS_FILESYSTEM 1\n        #elif __has_include(<experimental/filesystem>)\n            #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1\n        #endif\n\n        // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/\n        #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8\n            #undef JSON_HAS_FILESYSTEM\n            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM\n        #endif\n\n        // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support\n        #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8\n            #undef JSON_HAS_FILESYSTEM\n            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM\n        #endif\n\n        // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support\n        #if defined(__clang_major__) && __clang_major__ < 7\n            #undef JSON_HAS_FILESYSTEM\n            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM\n        #endif\n\n        // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support\n        #if defined(_MSC_VER) && _MSC_VER < 1914\n            #undef JSON_HAS_FILESYSTEM\n            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM\n        #endif\n\n        // no filesystem support before iOS 13\n        #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000\n            #undef JSON_HAS_FILESYSTEM\n            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM\n        #endif\n\n        // no filesystem support before macOS Catalina\n        #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500\n            #undef JSON_HAS_FILESYSTEM\n            #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM\n        #endif\n    #endif\n#endif\n\n#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM\n    #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0\n#endif\n\n#ifndef JSON_HAS_FILESYSTEM\n    #define JSON_HAS_FILESYSTEM 0\n#endif\n\n#ifndef JSON_HAS_THREE_WAY_COMPARISON\n    #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \\\n        && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L\n        #define JSON_HAS_THREE_WAY_COMPARISON 1\n    #else\n        #define JSON_HAS_THREE_WAY_COMPARISON 0\n    #endif\n#endif\n\n#ifndef JSON_HAS_RANGES\n    // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error\n    #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427\n        #define JSON_HAS_RANGES 0\n    #elif defined(__cpp_lib_ranges)\n        #define JSON_HAS_RANGES 1\n    #else\n        #define JSON_HAS_RANGES 0\n    #endif\n#endif\n\n#ifdef JSON_HAS_CPP_17\n    #define JSON_INLINE_VARIABLE inline\n#else\n    #define JSON_INLINE_VARIABLE\n#endif\n\n#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)\n    #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]\n#else\n    #define JSON_NO_UNIQUE_ADDRESS\n#endif\n\n// disable documentation warnings on clang\n#if defined(__clang__)\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wdocumentation\"\n    #pragma clang diagnostic ignored \"-Wdocumentation-unknown-command\"\n#endif\n\n// allow disabling exceptions\n#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)\n    #define JSON_THROW(exception) throw exception\n    #define JSON_TRY try\n    #define JSON_CATCH(exception) catch(exception)\n    #define JSON_INTERNAL_CATCH(exception) catch(exception)\n#else\n    #include <cstdlib>\n    #define JSON_THROW(exception) std::abort()\n    #define JSON_TRY if(true)\n    #define JSON_CATCH(exception) if(false)\n    #define JSON_INTERNAL_CATCH(exception) if(false)\n#endif\n\n// override exception macros\n#if defined(JSON_THROW_USER)\n    #undef JSON_THROW\n    #define JSON_THROW JSON_THROW_USER\n#endif\n#if defined(JSON_TRY_USER)\n    #undef JSON_TRY\n    #define JSON_TRY JSON_TRY_USER\n#endif\n#if defined(JSON_CATCH_USER)\n    #undef JSON_CATCH\n    #define JSON_CATCH JSON_CATCH_USER\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_CATCH_USER\n#endif\n#if defined(JSON_INTERNAL_CATCH_USER)\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER\n#endif\n\n// allow overriding assert\n#if !defined(JSON_ASSERT)\n    #include <cassert> // assert\n    #define JSON_ASSERT(x) assert(x)\n#endif\n\n// allow to access some private functions (needed by the test suite)\n#if defined(JSON_TESTS_PRIVATE)\n    #define JSON_PRIVATE_UNLESS_TESTED public\n#else\n    #define JSON_PRIVATE_UNLESS_TESTED private\n#endif\n\n/*!\n@brief macro to briefly define a mapping between an enum and JSON\n@def NLOHMANN_JSON_SERIALIZE_ENUM\n@since version 3.4.0\n*/\n#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                            \\\n    template<typename BasicJsonType>                                                            \\\n    inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                   \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool  \\\n        {                                                                                       \\\n            return ej_pair.first == e;                                                          \\\n        });                                                                                     \\\n        j = ((it != std::end(m)) ? it : std::begin(m))->second;                                 \\\n    }                                                                                           \\\n    template<typename BasicJsonType>                                                            \\\n    inline void from_json(const BasicJsonType& j, ENUM_TYPE& e)                                 \\\n    {                                                                                           \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");          \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                     \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                      \\\n                               [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \\\n        {                                                                                       \\\n            return ej_pair.second == j;                                                         \\\n        });                                                                                     \\\n        e = ((it != std::end(m)) ? it : std::begin(m))->first;                                  \\\n    }\n\n// Ugly macros to avoid uglier copy-paste when specializing basic_json. They\n// may be removed in the future once the class is split.\n\n#define NLOHMANN_BASIC_JSON_TPL_DECLARATION                                \\\n    template<template<typename, typename, typename...> class ObjectType,   \\\n             template<typename, typename...> class ArrayType,              \\\n             class StringType, class BooleanType, class NumberIntegerType, \\\n             class NumberUnsignedType, class NumberFloatType,              \\\n             template<typename> class AllocatorType,                       \\\n             template<typename, typename = void> class JSONSerializer,     \\\n             class BinaryType>\n\n#define NLOHMANN_BASIC_JSON_TPL                                            \\\n    basic_json<ObjectType, ArrayType, StringType, BooleanType,             \\\n    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \\\n    AllocatorType, JSONSerializer, BinaryType>\n\n// Macros to simplify conversion from/to types\n\n#define NLOHMANN_JSON_EXPAND( x ) x\n#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME\n#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \\\n        NLOHMANN_JSON_PASTE64, \\\n        NLOHMANN_JSON_PASTE63, \\\n        NLOHMANN_JSON_PASTE62, \\\n        NLOHMANN_JSON_PASTE61, \\\n        NLOHMANN_JSON_PASTE60, \\\n        NLOHMANN_JSON_PASTE59, \\\n        NLOHMANN_JSON_PASTE58, \\\n        NLOHMANN_JSON_PASTE57, \\\n        NLOHMANN_JSON_PASTE56, \\\n        NLOHMANN_JSON_PASTE55, \\\n        NLOHMANN_JSON_PASTE54, \\\n        NLOHMANN_JSON_PASTE53, \\\n        NLOHMANN_JSON_PASTE52, \\\n        NLOHMANN_JSON_PASTE51, \\\n        NLOHMANN_JSON_PASTE50, \\\n        NLOHMANN_JSON_PASTE49, \\\n        NLOHMANN_JSON_PASTE48, \\\n        NLOHMANN_JSON_PASTE47, \\\n        NLOHMANN_JSON_PASTE46, \\\n        NLOHMANN_JSON_PASTE45, \\\n        NLOHMANN_JSON_PASTE44, \\\n        NLOHMANN_JSON_PASTE43, \\\n        NLOHMANN_JSON_PASTE42, \\\n        NLOHMANN_JSON_PASTE41, \\\n        NLOHMANN_JSON_PASTE40, \\\n        NLOHMANN_JSON_PASTE39, \\\n        NLOHMANN_JSON_PASTE38, \\\n        NLOHMANN_JSON_PASTE37, \\\n        NLOHMANN_JSON_PASTE36, \\\n        NLOHMANN_JSON_PASTE35, \\\n        NLOHMANN_JSON_PASTE34, \\\n        NLOHMANN_JSON_PASTE33, \\\n        NLOHMANN_JSON_PASTE32, \\\n        NLOHMANN_JSON_PASTE31, \\\n        NLOHMANN_JSON_PASTE30, \\\n        NLOHMANN_JSON_PASTE29, \\\n        NLOHMANN_JSON_PASTE28, \\\n        NLOHMANN_JSON_PASTE27, \\\n        NLOHMANN_JSON_PASTE26, \\\n        NLOHMANN_JSON_PASTE25, \\\n        NLOHMANN_JSON_PASTE24, \\\n        NLOHMANN_JSON_PASTE23, \\\n        NLOHMANN_JSON_PASTE22, \\\n        NLOHMANN_JSON_PASTE21, \\\n        NLOHMANN_JSON_PASTE20, \\\n        NLOHMANN_JSON_PASTE19, \\\n        NLOHMANN_JSON_PASTE18, \\\n        NLOHMANN_JSON_PASTE17, \\\n        NLOHMANN_JSON_PASTE16, \\\n        NLOHMANN_JSON_PASTE15, \\\n        NLOHMANN_JSON_PASTE14, \\\n        NLOHMANN_JSON_PASTE13, \\\n        NLOHMANN_JSON_PASTE12, \\\n        NLOHMANN_JSON_PASTE11, \\\n        NLOHMANN_JSON_PASTE10, \\\n        NLOHMANN_JSON_PASTE9, \\\n        NLOHMANN_JSON_PASTE8, \\\n        NLOHMANN_JSON_PASTE7, \\\n        NLOHMANN_JSON_PASTE6, \\\n        NLOHMANN_JSON_PASTE5, \\\n        NLOHMANN_JSON_PASTE4, \\\n        NLOHMANN_JSON_PASTE3, \\\n        NLOHMANN_JSON_PASTE2, \\\n        NLOHMANN_JSON_PASTE1)(__VA_ARGS__))\n#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)\n#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)\n#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)\n#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)\n#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)\n#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)\n#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)\n#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)\n#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)\n#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)\n#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)\n#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)\n#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)\n#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)\n#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)\n#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)\n#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)\n#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)\n#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)\n#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)\n#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)\n#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)\n#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)\n#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)\n#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)\n#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)\n#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)\n#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)\n#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)\n#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)\n#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)\n#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)\n#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)\n#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)\n#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)\n#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)\n#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)\n#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)\n#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)\n#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)\n#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)\n#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)\n#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)\n#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)\n#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)\n#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)\n#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)\n#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)\n#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)\n#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)\n#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)\n#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)\n#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)\n#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)\n#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)\n#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)\n#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)\n#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)\n#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)\n#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)\n#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)\n#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)\n#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)\n\n#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;\n#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);\n#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);\n\n/*!\n@brief macro\n@def NLOHMANN_DEFINE_TYPE_INTRUSIVE\n@since version 3.9.0\n*/\n#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...)  \\\n    friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }\n\n#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...)  \\\n    friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }\n\n/*!\n@brief macro\n@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE\n@since version 3.9.0\n*/\n#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...)  \\\n    inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }\n\n#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...)  \\\n    inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \\\n    inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }\n\n\n// inspired from https://stackoverflow.com/a/26745591\n// allows to call any std function as if (e.g. with begin):\n// using std::begin; begin(x);\n//\n// it allows using the detected idiom to retrieve the return type\n// of such an expression\n#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name)                                 \\\n    namespace detail {                                                            \\\n    using std::std_name;                                                          \\\n    \\\n    template<typename... T>                                                       \\\n    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \\\n    }                                                                             \\\n    \\\n    namespace detail2 {                                                           \\\n    struct std_name##_tag                                                         \\\n    {                                                                             \\\n    };                                                                            \\\n    \\\n    template<typename... T>                                                       \\\n    std_name##_tag std_name(T&&...);                                              \\\n    \\\n    template<typename... T>                                                       \\\n    using result_of_##std_name = decltype(std_name(std::declval<T>()...));        \\\n    \\\n    template<typename... T>                                                       \\\n    struct would_call_std_##std_name                                              \\\n    {                                                                             \\\n        static constexpr auto const value = ::nlohmann::detail::                  \\\n                                            is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \\\n    };                                                                            \\\n    } /* namespace detail2 */ \\\n    \\\n    template<typename... T>                                                       \\\n    struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...>   \\\n    {                                                                             \\\n    }\n\n#ifndef JSON_USE_IMPLICIT_CONVERSIONS\n    #define JSON_USE_IMPLICIT_CONVERSIONS 1\n#endif\n\n#if JSON_USE_IMPLICIT_CONVERSIONS\n    #define JSON_EXPLICIT\n#else\n    #define JSON_EXPLICIT explicit\n#endif\n\n#ifndef JSON_DISABLE_ENUM_SERIALIZATION\n    #define JSON_DISABLE_ENUM_SERIALIZATION 0\n#endif\n\n#ifndef JSON_USE_GLOBAL_UDLS\n    #define JSON_USE_GLOBAL_UDLS 1\n#endif\n\n#if JSON_HAS_THREE_WAY_COMPARISON\n    #include <compare> // partial_ordering\n#endif\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n///////////////////////////\n// JSON type enumeration //\n///////////////////////////\n\n/*!\n@brief the JSON type enumeration\n\nThis enumeration collects the different JSON types. It is internally used to\ndistinguish the stored values, and the functions @ref basic_json::is_null(),\n@ref basic_json::is_object(), @ref basic_json::is_array(),\n@ref basic_json::is_string(), @ref basic_json::is_boolean(),\n@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),\n@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),\n@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and\n@ref basic_json::is_structured() rely on it.\n\n@note There are three enumeration entries (number_integer, number_unsigned, and\nnumber_float), because the library distinguishes these three types for numbers:\n@ref basic_json::number_unsigned_t is used for unsigned integers,\n@ref basic_json::number_integer_t is used for signed integers, and\n@ref basic_json::number_float_t is used for floating-point numbers or to\napproximate integers which do not fit in the limits of their respective type.\n\n@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON\nvalue with the default value for a given type\n\n@since version 1.0.0\n*/\nenum class value_t : std::uint8_t\n{\n    null,             ///< null value\n    object,           ///< object (unordered set of name/value pairs)\n    array,            ///< array (ordered collection of values)\n    string,           ///< string value\n    boolean,          ///< boolean value\n    number_integer,   ///< number value (signed integer)\n    number_unsigned,  ///< number value (unsigned integer)\n    number_float,     ///< number value (floating-point)\n    binary,           ///< binary array (ordered collection of bytes)\n    discarded         ///< discarded by the parser callback function\n};\n\n/*!\n@brief comparison operator for JSON types\n\nReturns an ordering that is similar to Python:\n- order: null < boolean < number < object < array < string < binary\n- furthermore, each type is not smaller than itself\n- discarded values are not comparable\n- binary is represented as a b\"\" string in python and directly comparable to a\n  string; however, making a binary array directly comparable with a string would\n  be surprising behavior in a JSON file.\n\n@since version 1.0.0\n*/\n#if JSON_HAS_THREE_WAY_COMPARISON\n    inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD*\n#else\n    inline bool operator<(const value_t lhs, const value_t rhs) noexcept\n#endif\n{\n    static constexpr std::array<std::uint8_t, 9> order = {{\n            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,\n            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,\n            6 /* binary */\n        }\n    };\n\n    const auto l_index = static_cast<std::size_t>(lhs);\n    const auto r_index = static_cast<std::size_t>(rhs);\n#if JSON_HAS_THREE_WAY_COMPARISON\n    if (l_index < order.size() && r_index < order.size())\n    {\n        return order[l_index] <=> order[r_index]; // *NOPAD*\n    }\n    return std::partial_ordering::unordered;\n#else\n    return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];\n#endif\n}\n\n// GCC selects the built-in operator< over an operator rewritten from\n// a user-defined spaceship operator\n// Clang, MSVC, and ICC select the rewritten candidate\n// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)\n#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__)\ninline bool operator<(const value_t lhs, const value_t rhs) noexcept\n{\n    return std::is_lt(lhs <=> rhs); // *NOPAD*\n}\n#endif\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/string_escape.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/*!\n@brief replace all occurrences of a substring by another string\n\n@param[in,out] s  the string to manipulate; changed so that all\n               occurrences of @a f are replaced with @a t\n@param[in]     f  the substring to replace with @a t\n@param[in]     t  the string to replace @a f\n\n@pre The search string @a f must not be empty. **This precondition is\nenforced with an assertion.**\n\n@since version 2.0.0\n*/\ntemplate<typename StringType>\ninline void replace_substring(StringType& s, const StringType& f,\n                              const StringType& t)\n{\n    JSON_ASSERT(!f.empty());\n    for (auto pos = s.find(f);                // find first occurrence of f\n            pos != StringType::npos;          // make sure f was found\n            s.replace(pos, f.size(), t),      // replace with t, and\n            pos = s.find(f, pos + t.size()))  // find next occurrence of f\n    {}\n}\n\n/*!\n * @brief string escaping as described in RFC 6901 (Sect. 4)\n * @param[in] s string to escape\n * @return    escaped string\n *\n * Note the order of escaping \"~\" to \"~0\" and \"/\" to \"~1\" is important.\n */\ntemplate<typename StringType>\ninline StringType escape(StringType s)\n{\n    replace_substring(s, StringType{\"~\"}, StringType{\"~0\"});\n    replace_substring(s, StringType{\"/\"}, StringType{\"~1\"});\n    return s;\n}\n\n/*!\n * @brief string unescaping as described in RFC 6901 (Sect. 4)\n * @param[in] s string to unescape\n * @return    unescaped string\n *\n * Note the order of escaping \"~1\" to \"/\" and \"~0\" to \"~\" is important.\n */\ntemplate<typename StringType>\nstatic void unescape(StringType& s)\n{\n    replace_substring(s, StringType{\"~1\"}, StringType{\"/\"});\n    replace_substring(s, StringType{\"~0\"}, StringType{\"~\"});\n}\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/input/position_t.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <cstddef> // size_t\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/// struct to capture the start position of the current token\nstruct position_t\n{\n    /// the total number of characters read\n    std::size_t chars_read_total = 0;\n    /// the number of characters read in the current line\n    std::size_t chars_read_current_line = 0;\n    /// the number of lines read\n    std::size_t lines_read = 0;\n\n    /// conversion to size_t to preserve SAX interface\n    constexpr operator size_t() const\n    {\n        return chars_read_total;\n    }\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-FileCopyrightText: 2018 The Abseil Authors\n// SPDX-License-Identifier: MIT\n\n\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type\n#include <utility> // index_sequence, make_index_sequence, index_sequence_for\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename T>\nusing uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;\n\n#ifdef JSON_HAS_CPP_14\n\n// the following utilities are natively available in C++14\nusing std::enable_if_t;\nusing std::index_sequence;\nusing std::make_index_sequence;\nusing std::index_sequence_for;\n\n#else\n\n// alias templates to reduce boilerplate\ntemplate<bool B, typename T = void>\nusing enable_if_t = typename std::enable_if<B, T>::type;\n\n// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h\n// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0.\n\n//// START OF CODE FROM GOOGLE ABSEIL\n\n// integer_sequence\n//\n// Class template representing a compile-time integer sequence. An instantiation\n// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its\n// type through its template arguments (which is a common need when\n// working with C++11 variadic templates). `absl::integer_sequence` is designed\n// to be a drop-in replacement for C++14's `std::integer_sequence`.\n//\n// Example:\n//\n//   template< class T, T... Ints >\n//   void user_function(integer_sequence<T, Ints...>);\n//\n//   int main()\n//   {\n//     // user_function's `T` will be deduced to `int` and `Ints...`\n//     // will be deduced to `0, 1, 2, 3, 4`.\n//     user_function(make_integer_sequence<int, 5>());\n//   }\ntemplate <typename T, T... Ints>\nstruct integer_sequence\n{\n    using value_type = T;\n    static constexpr std::size_t size() noexcept\n    {\n        return sizeof...(Ints);\n    }\n};\n\n// index_sequence\n//\n// A helper template for an `integer_sequence` of `size_t`,\n// `absl::index_sequence` is designed to be a drop-in replacement for C++14's\n// `std::index_sequence`.\ntemplate <size_t... Ints>\nusing index_sequence = integer_sequence<size_t, Ints...>;\n\nnamespace utility_internal\n{\n\ntemplate <typename Seq, size_t SeqSize, size_t Rem>\nstruct Extend;\n\n// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.\ntemplate <typename T, T... Ints, size_t SeqSize>\nstruct Extend<integer_sequence<T, Ints...>, SeqSize, 0>\n{\n    using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >;\n};\n\ntemplate <typename T, T... Ints, size_t SeqSize>\nstruct Extend<integer_sequence<T, Ints...>, SeqSize, 1>\n{\n    using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >;\n};\n\n// Recursion helper for 'make_integer_sequence<T, N>'.\n// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.\ntemplate <typename T, size_t N>\nstruct Gen\n{\n    using type =\n        typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type;\n};\n\ntemplate <typename T>\nstruct Gen<T, 0>\n{\n    using type = integer_sequence<T>;\n};\n\n}  // namespace utility_internal\n\n// Compile-time sequences of integers\n\n// make_integer_sequence\n//\n// This template alias is equivalent to\n// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in\n// replacement for C++14's `std::make_integer_sequence`.\ntemplate <typename T, T N>\nusing make_integer_sequence = typename utility_internal::Gen<T, N>::type;\n\n// make_index_sequence\n//\n// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,\n// and is designed to be a drop-in replacement for C++14's\n// `std::make_index_sequence`.\ntemplate <size_t N>\nusing make_index_sequence = make_integer_sequence<size_t, N>;\n\n// index_sequence_for\n//\n// Converts a typename pack into an index sequence of the same length, and\n// is designed to be a drop-in replacement for C++14's\n// `std::index_sequence_for()`\ntemplate <typename... Ts>\nusing index_sequence_for = make_index_sequence<sizeof...(Ts)>;\n\n//// END OF CODE FROM GOOGLE ABSEIL\n\n#endif\n\n// dispatch utility (taken from ranges-v3)\ntemplate<unsigned N> struct priority_tag : priority_tag < N - 1 > {};\ntemplate<> struct priority_tag<0> {};\n\n// taken from ranges-v3\ntemplate<typename T>\nstruct static_const\n{\n    static JSON_INLINE_VARIABLE constexpr T value{};\n};\n\n#ifndef JSON_HAS_CPP_17\n    template<typename T>\n    constexpr T static_const<T>::value;\n#endif\n\ntemplate<typename T, typename... Args>\ninline constexpr std::array<T, sizeof...(Args)> make_array(Args&& ... args)\n{\n    return std::array<T, sizeof...(Args)> {{static_cast<T>(std::forward<Args>(args))...}};\n}\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <limits> // numeric_limits\n#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type\n#include <utility> // declval\n#include <tuple> // tuple\n\n// #include <nlohmann/detail/iterators/iterator_traits.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <iterator> // random_access_iterator_tag\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n// #include <nlohmann/detail/meta/void_t.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename It, typename = void>\nstruct iterator_types {};\n\ntemplate<typename It>\nstruct iterator_types <\n    It,\n    void_t<typename It::difference_type, typename It::value_type, typename It::pointer,\n    typename It::reference, typename It::iterator_category >>\n{\n    using difference_type = typename It::difference_type;\n    using value_type = typename It::value_type;\n    using pointer = typename It::pointer;\n    using reference = typename It::reference;\n    using iterator_category = typename It::iterator_category;\n};\n\n// This is required as some compilers implement std::iterator_traits in a way that\n// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.\ntemplate<typename T, typename = void>\nstruct iterator_traits\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>\n            : iterator_types<T>\n{\n};\n\ntemplate<typename T>\nstruct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>\n{\n    using iterator_category = std::random_access_iterator_tag;\n    using value_type = T;\n    using difference_type = ptrdiff_t;\n    using pointer = T*;\n    using reference = T&;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/call_std/begin.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\nNLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin);\n\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/meta/call_std/end.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\nNLOHMANN_CAN_CALL_STD_FUNC_IMPL(end);\n\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n// #include <nlohmann/json_fwd.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_\n    #define INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n    #include <cstdint> // int64_t, uint64_t\n    #include <map> // map\n    #include <memory> // allocator\n    #include <string> // string\n    #include <vector> // vector\n\n    // #include <nlohmann/detail/abi_macros.hpp>\n\n\n    /*!\n    @brief namespace for Niels Lohmann\n    @see https://github.com/nlohmann\n    @since version 1.0.0\n    */\n    NLOHMANN_JSON_NAMESPACE_BEGIN\n\n    /*!\n    @brief default JSONSerializer template argument\n\n    This serializer ignores the template arguments and uses ADL\n    ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))\n    for serialization.\n    */\n    template<typename T = void, typename SFINAE = void>\n    struct adl_serializer;\n\n    /// a class to store JSON values\n    /// @sa https://json.nlohmann.me/api/basic_json/\n    template<template<typename U, typename V, typename... Args> class ObjectType =\n    std::map,\n    template<typename U, typename... Args> class ArrayType = std::vector,\n    class StringType = std::string, class BooleanType = bool,\n    class NumberIntegerType = std::int64_t,\n    class NumberUnsignedType = std::uint64_t,\n    class NumberFloatType = double,\n    template<typename U> class AllocatorType = std::allocator,\n    template<typename T, typename SFINAE = void> class JSONSerializer =\n    adl_serializer,\n    class BinaryType = std::vector<std::uint8_t>>\n    class basic_json;\n\n    /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document\n    /// @sa https://json.nlohmann.me/api/json_pointer/\n    template<typename RefStringType>\n    class json_pointer;\n\n    /*!\n    @brief default specialization\n    @sa https://json.nlohmann.me/api/json/\n    */\n    using json = basic_json<>;\n\n    /// @brief a minimal map-like container that preserves insertion order\n    /// @sa https://json.nlohmann.me/api/ordered_map/\n    template<class Key, class T, class IgnoredLess, class Allocator>\n    struct ordered_map;\n\n    /// @brief specialization that maintains the insertion order of object keys\n    /// @sa https://json.nlohmann.me/api/ordered_json/\n    using ordered_json = basic_json<nlohmann::ordered_map>;\n\n    NLOHMANN_JSON_NAMESPACE_END\n\n#endif  // INCLUDE_NLOHMANN_JSON_FWD_HPP_\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n/*!\n@brief detail namespace with internal helper functions\n\nThis namespace collects functions that should not be exposed,\nimplementations of some @ref basic_json methods, and meta-programming helpers.\n\n@since version 2.1.0\n*/\nnamespace detail\n{\n\n/////////////\n// helpers //\n/////////////\n\n// Note to maintainers:\n//\n// Every trait in this file expects a non CV-qualified type.\n// The only exceptions are in the 'aliases for detected' section\n// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))\n//\n// In this case, T has to be properly CV-qualified to constraint the function arguments\n// (e.g. to_json(BasicJsonType&, const T&))\n\ntemplate<typename> struct is_basic_json : std::false_type {};\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstruct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};\n\n// used by exceptions create() member functions\n// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t\n// false_type otherwise\ntemplate<typename BasicJsonContext>\nstruct is_basic_json_context :\n    std::integral_constant < bool,\n    is_basic_json<typename std::remove_cv<typename std::remove_pointer<BasicJsonContext>::type>::type>::value\n    || std::is_same<BasicJsonContext, std::nullptr_t>::value >\n{};\n\n//////////////////////\n// json_ref helpers //\n//////////////////////\n\ntemplate<typename>\nclass json_ref;\n\ntemplate<typename>\nstruct is_json_ref : std::false_type {};\n\ntemplate<typename T>\nstruct is_json_ref<json_ref<T>> : std::true_type {};\n\n//////////////////////////\n// aliases for detected //\n//////////////////////////\n\ntemplate<typename T>\nusing mapped_type_t = typename T::mapped_type;\n\ntemplate<typename T>\nusing key_type_t = typename T::key_type;\n\ntemplate<typename T>\nusing value_type_t = typename T::value_type;\n\ntemplate<typename T>\nusing difference_type_t = typename T::difference_type;\n\ntemplate<typename T>\nusing pointer_t = typename T::pointer;\n\ntemplate<typename T>\nusing reference_t = typename T::reference;\n\ntemplate<typename T>\nusing iterator_category_t = typename T::iterator_category;\n\ntemplate<typename T, typename... Args>\nusing to_json_function = decltype(T::to_json(std::declval<Args>()...));\n\ntemplate<typename T, typename... Args>\nusing from_json_function = decltype(T::from_json(std::declval<Args>()...));\n\ntemplate<typename T, typename U>\nusing get_template_function = decltype(std::declval<T>().template get<U>());\n\n// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_from_json : std::false_type {};\n\n// trait checking if j.get<T> is valid\n// use this trait instead of std::is_constructible or std::is_convertible,\n// both rely on, or make use of implicit conversions, and thus fail when T\n// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)\ntemplate <typename BasicJsonType, typename T>\nstruct is_getable\n{\n    static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;\n};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, from_json_function, serializer,\n        const BasicJsonType&, T&>::value;\n};\n\n// This trait checks if JSONSerializer<T>::from_json(json const&) exists\n// this overload is used for non-default-constructible user-defined-types\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_non_default_from_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<T, from_json_function, serializer,\n        const BasicJsonType&>::value;\n};\n\n// This trait checks if BasicJsonType::json_serializer<T>::to_json exists\n// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.\ntemplate<typename BasicJsonType, typename T, typename = void>\nstruct has_to_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,\n        T>::value;\n};\n\ntemplate<typename T>\nusing detect_key_compare = typename T::key_compare;\n\ntemplate<typename T>\nstruct has_key_compare : std::integral_constant<bool, is_detected<detect_key_compare, T>::value> {};\n\n// obtains the actual object key comparator\ntemplate<typename BasicJsonType>\nstruct actual_object_comparator\n{\n    using object_t = typename BasicJsonType::object_t;\n    using object_comparator_t = typename BasicJsonType::default_object_comparator_t;\n    using type = typename std::conditional < has_key_compare<object_t>::value,\n          typename object_t::key_compare, object_comparator_t>::type;\n};\n\ntemplate<typename BasicJsonType>\nusing actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;\n\n///////////////////\n// is_ functions //\n///////////////////\n\n// https://en.cppreference.com/w/cpp/types/conjunction\ntemplate<class...> struct conjunction : std::true_type { };\ntemplate<class B> struct conjunction<B> : B { };\ntemplate<class B, class... Bn>\nstruct conjunction<B, Bn...>\n: std::conditional<static_cast<bool>(B::value), conjunction<Bn...>, B>::type {};\n\n// https://en.cppreference.com/w/cpp/types/negation\ntemplate<class B> struct negation : std::integral_constant < bool, !B::value > { };\n\n// Reimplementation of is_constructible and is_default_constructible, due to them being broken for\n// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).\n// This causes compile errors in e.g. clang 3.5 or gcc 4.9.\ntemplate <typename T>\nstruct is_default_constructible : std::is_default_constructible<T> {};\n\ntemplate <typename T1, typename T2>\nstruct is_default_constructible<std::pair<T1, T2>>\n            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};\n\ntemplate <typename T1, typename T2>\nstruct is_default_constructible<const std::pair<T1, T2>>\n            : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};\n\ntemplate <typename... Ts>\nstruct is_default_constructible<std::tuple<Ts...>>\n            : conjunction<is_default_constructible<Ts>...> {};\n\ntemplate <typename... Ts>\nstruct is_default_constructible<const std::tuple<Ts...>>\n            : conjunction<is_default_constructible<Ts>...> {};\n\n\ntemplate <typename T, typename... Args>\nstruct is_constructible : std::is_constructible<T, Args...> {};\n\ntemplate <typename T1, typename T2>\nstruct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};\n\ntemplate <typename T1, typename T2>\nstruct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};\n\ntemplate <typename... Ts>\nstruct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};\n\ntemplate <typename... Ts>\nstruct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};\n\n\ntemplate<typename T, typename = void>\nstruct is_iterator_traits : std::false_type {};\n\ntemplate<typename T>\nstruct is_iterator_traits<iterator_traits<T>>\n{\n  private:\n    using traits = iterator_traits<T>;\n\n  public:\n    static constexpr auto value =\n        is_detected<value_type_t, traits>::value &&\n        is_detected<difference_type_t, traits>::value &&\n        is_detected<pointer_t, traits>::value &&\n        is_detected<iterator_category_t, traits>::value &&\n        is_detected<reference_t, traits>::value;\n};\n\ntemplate<typename T>\nstruct is_range\n{\n  private:\n    using t_ref = typename std::add_lvalue_reference<T>::type;\n\n    using iterator = detected_t<result_of_begin, t_ref>;\n    using sentinel = detected_t<result_of_end, t_ref>;\n\n    // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator\n    // and https://en.cppreference.com/w/cpp/iterator/sentinel_for\n    // but reimplementing these would be too much work, as a lot of other concepts are used underneath\n    static constexpr auto is_iterator_begin =\n        is_iterator_traits<iterator_traits<iterator>>::value;\n\n  public:\n    static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin;\n};\n\ntemplate<typename R>\nusing iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R&>())>>;\n\ntemplate<typename T>\nusing range_value_t = value_type_t<iterator_traits<iterator_t<T>>>;\n\n// The following implementation of is_complete_type is taken from\n// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/\n// and is written by Xiang Fan who agreed to using it in this library.\n\ntemplate<typename T, typename = void>\nstruct is_complete_type : std::false_type {};\n\ntemplate<typename T>\nstruct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType,\n         typename = void>\nstruct is_compatible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type_impl <\n    BasicJsonType, CompatibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&\n    is_detected<key_type_t, CompatibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    // macOS's is_constructible does not play well with nonesuch...\n    static constexpr bool value =\n        is_constructible<typename object_t::key_type,\n        typename CompatibleObjectType::key_type>::value &&\n        is_constructible<typename object_t::mapped_type,\n        typename CompatibleObjectType::mapped_type>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type\n    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         typename = void>\nstruct is_constructible_object_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type_impl <\n    BasicJsonType, ConstructibleObjectType,\n    enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&\n    is_detected<key_type_t, ConstructibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    static constexpr bool value =\n        (is_default_constructible<ConstructibleObjectType>::value &&\n         (std::is_move_assignable<ConstructibleObjectType>::value ||\n          std::is_copy_assignable<ConstructibleObjectType>::value) &&\n         (is_constructible<typename ConstructibleObjectType::key_type,\n          typename object_t::key_type>::value &&\n          std::is_same <\n          typename object_t::mapped_type,\n          typename ConstructibleObjectType::mapped_type >::value)) ||\n        (has_from_json<BasicJsonType,\n         typename ConstructibleObjectType::mapped_type>::value ||\n         has_non_default_from_json <\n         BasicJsonType,\n         typename ConstructibleObjectType::mapped_type >::value);\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type\n    : is_constructible_object_type_impl<BasicJsonType,\n      ConstructibleObjectType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleStringType>\nstruct is_compatible_string_type\n{\n    static constexpr auto value =\n        is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type\n{\n    // launder type through decltype() to fix compilation failure on ICPC\n#ifdef __INTEL_COMPILER\n    using laundered_type = decltype(std::declval<ConstructibleStringType>());\n#else\n    using laundered_type = ConstructibleStringType;\n#endif\n\n    static constexpr auto value =\n        conjunction <\n        is_constructible<laundered_type, typename BasicJsonType::string_t>,\n        is_detected_exact<typename BasicJsonType::string_t::value_type,\n        value_type_t, laundered_type >>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType, typename = void>\nstruct is_compatible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type_impl <\n    BasicJsonType, CompatibleArrayType,\n    enable_if_t <\n    is_detected<iterator_t, CompatibleArrayType>::value&&\n    is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&\n// special case for types like std::filesystem::path whose iterator's value_type are themselves\n// c.f. https://github.com/nlohmann/json/pull/3073\n    !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>\n{\n    static constexpr bool value =\n        is_constructible<BasicJsonType,\n        range_value_t<CompatibleArrayType>>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type\n    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType, typename = void>\nstruct is_constructible_array_type_impl : std::false_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t<std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value >>\n            : std::true_type {};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t < !std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value&&\n    !is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value&&\n    is_default_constructible<ConstructibleArrayType>::value&&\n(std::is_move_assignable<ConstructibleArrayType>::value ||\n std::is_copy_assignable<ConstructibleArrayType>::value)&&\nis_detected<iterator_t, ConstructibleArrayType>::value&&\nis_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value&&\nis_detected<range_value_t, ConstructibleArrayType>::value&&\n// special case for types like std::filesystem::path whose iterator's value_type are themselves\n// c.f. https://github.com/nlohmann/json/pull/3073\n!std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value&&\n        is_complete_type <\n        detected_t<range_value_t, ConstructibleArrayType >>::value >>\n{\n    using value_type = range_value_t<ConstructibleArrayType>;\n\n    static constexpr bool value =\n        std::is_same<value_type,\n        typename BasicJsonType::array_t::value_type>::value ||\n        has_from_json<BasicJsonType,\n        value_type>::value ||\n        has_non_default_from_json <\n        BasicJsonType,\n        value_type >::value;\n};\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type\n    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType,\n         typename = void>\nstruct is_compatible_integer_type_impl : std::false_type {};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type_impl <\n    RealIntegerType, CompatibleNumberIntegerType,\n    enable_if_t < std::is_integral<RealIntegerType>::value&&\n    std::is_integral<CompatibleNumberIntegerType>::value&&\n    !std::is_same<bool, CompatibleNumberIntegerType>::value >>\n{\n    // is there an assert somewhere on overflows?\n    using RealLimits = std::numeric_limits<RealIntegerType>;\n    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;\n\n    static constexpr auto value =\n        is_constructible<RealIntegerType,\n        CompatibleNumberIntegerType>::value &&\n        CompatibleLimits::is_integer &&\n        RealLimits::is_signed == CompatibleLimits::is_signed;\n};\n\ntemplate<typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type\n    : is_compatible_integer_type_impl<RealIntegerType,\n      CompatibleNumberIntegerType> {};\n\ntemplate<typename BasicJsonType, typename CompatibleType, typename = void>\nstruct is_compatible_type_impl: std::false_type {};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type_impl <\n    BasicJsonType, CompatibleType,\n    enable_if_t<is_complete_type<CompatibleType>::value >>\n{\n    static constexpr bool value =\n        has_to_json<BasicJsonType, CompatibleType>::value;\n};\n\ntemplate<typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type\n    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};\n\ntemplate<typename T1, typename T2>\nstruct is_constructible_tuple : std::false_type {};\n\ntemplate<typename T1, typename... Args>\nstruct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct is_json_iterator_of : std::false_type {};\n\ntemplate<typename BasicJsonType>\nstruct is_json_iterator_of<BasicJsonType, typename BasicJsonType::iterator> : std::true_type {};\n\ntemplate<typename BasicJsonType>\nstruct is_json_iterator_of<BasicJsonType, typename BasicJsonType::const_iterator> : std::true_type\n{};\n\n// checks if a given type T is a template specialization of Primary\ntemplate<template <typename...> class Primary, typename T>\nstruct is_specialization_of : std::false_type {};\n\ntemplate<template <typename...> class Primary, typename... Args>\nstruct is_specialization_of<Primary, Primary<Args...>> : std::true_type {};\n\ntemplate<typename T>\nusing is_json_pointer = is_specialization_of<::nlohmann::json_pointer, uncvref_t<T>>;\n\n// checks if A and B are comparable using Compare functor\ntemplate<typename Compare, typename A, typename B, typename = void>\nstruct is_comparable : std::false_type {};\n\ntemplate<typename Compare, typename A, typename B>\nstruct is_comparable<Compare, A, B, void_t<\ndecltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>())),\ndecltype(std::declval<Compare>()(std::declval<B>(), std::declval<A>()))\n>> : std::true_type {};\n\ntemplate<typename T>\nusing detect_is_transparent = typename T::is_transparent;\n\n// type trait to check if KeyType can be used as object key (without a BasicJsonType)\n// see is_usable_as_basic_json_key_type below\ntemplate<typename Comparator, typename ObjectKeyType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,\n         bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>\nusing is_usable_as_key_type = typename std::conditional <\n                              is_comparable<Comparator, ObjectKeyType, KeyTypeCVRef>::value\n                              && !(ExcludeObjectKeyType && std::is_same<KeyType,\n                                   ObjectKeyType>::value)\n                              && (!RequireTransparentComparator\n                                  || is_detected <detect_is_transparent, Comparator>::value)\n                              && !is_json_pointer<KeyType>::value,\n                              std::true_type,\n                              std::false_type >::type;\n\n// type trait to check if KeyType can be used as object key\n// true if:\n//   - KeyType is comparable with BasicJsonType::object_t::key_type\n//   - if ExcludeObjectKeyType is true, KeyType is not BasicJsonType::object_t::key_type\n//   - the comparator is transparent or RequireTransparentComparator is false\n//   - KeyType is not a JSON iterator or json_pointer\ntemplate<typename BasicJsonType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,\n         bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>\nusing is_usable_as_basic_json_key_type = typename std::conditional <\n        is_usable_as_key_type<typename BasicJsonType::object_comparator_t,\n        typename BasicJsonType::object_t::key_type, KeyTypeCVRef,\n        RequireTransparentComparator, ExcludeObjectKeyType>::value\n        && !is_json_iterator_of<BasicJsonType, KeyType>::value,\n        std::true_type,\n        std::false_type >::type;\n\ntemplate<typename ObjectType, typename KeyType>\nusing detect_erase_with_key_type = decltype(std::declval<ObjectType&>().erase(std::declval<KeyType>()));\n\n// type trait to check if object_t has an erase() member functions accepting KeyType\ntemplate<typename BasicJsonType, typename KeyType>\nusing has_erase_with_key_type = typename std::conditional <\n                                is_detected <\n                                detect_erase_with_key_type,\n                                typename BasicJsonType::object_t, KeyType >::value,\n                                std::true_type,\n                                std::false_type >::type;\n\n// a naive helper to check if a type is an ordered_map (exploits the fact that\n// ordered_map inherits capacity() from std::vector)\ntemplate <typename T>\nstruct is_ordered_map\n{\n    using one = char;\n\n    struct two\n    {\n        char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n    };\n\n    template <typename C> static one test( decltype(&C::capacity) ) ;\n    template <typename C> static two test(...);\n\n    enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n};\n\n// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)\ntemplate < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >\nT conditional_static_cast(U value)\n{\n    return static_cast<T>(value);\n}\n\ntemplate<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>\nT conditional_static_cast(U value)\n{\n    return value;\n}\n\ntemplate<typename... Types>\nusing all_integral = conjunction<std::is_integral<Types>...>;\n\ntemplate<typename... Types>\nusing all_signed = conjunction<std::is_signed<Types>...>;\n\ntemplate<typename... Types>\nusing all_unsigned = conjunction<std::is_unsigned<Types>...>;\n\n// there's a disjunction trait in another PR; replace when merged\ntemplate<typename... Types>\nusing same_sign = std::integral_constant < bool,\n      all_signed<Types...>::value || all_unsigned<Types...>::value >;\n\ntemplate<typename OfType, typename T>\nusing never_out_of_range = std::integral_constant < bool,\n      (std::is_signed<OfType>::value && (sizeof(T) < sizeof(OfType)))\n      || (same_sign<OfType, T>::value && sizeof(OfType) == sizeof(T)) >;\n\ntemplate<typename OfType, typename T,\n         bool OfTypeSigned = std::is_signed<OfType>::value,\n         bool TSigned = std::is_signed<T>::value>\nstruct value_in_range_of_impl2;\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl2<OfType, T, false, false>\n{\n    static constexpr bool test(T val)\n    {\n        using CommonType = typename std::common_type<OfType, T>::type;\n        return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());\n    }\n};\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl2<OfType, T, true, false>\n{\n    static constexpr bool test(T val)\n    {\n        using CommonType = typename std::common_type<OfType, T>::type;\n        return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());\n    }\n};\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl2<OfType, T, false, true>\n{\n    static constexpr bool test(T val)\n    {\n        using CommonType = typename std::common_type<OfType, T>::type;\n        return val >= 0 && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());\n    }\n};\n\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl2<OfType, T, true, true>\n{\n    static constexpr bool test(T val)\n    {\n        using CommonType = typename std::common_type<OfType, T>::type;\n        return static_cast<CommonType>(val) >= static_cast<CommonType>((std::numeric_limits<OfType>::min)())\n               && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());\n    }\n};\n\ntemplate<typename OfType, typename T,\n         bool NeverOutOfRange = never_out_of_range<OfType, T>::value,\n         typename = detail::enable_if_t<all_integral<OfType, T>::value>>\nstruct value_in_range_of_impl1;\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl1<OfType, T, false>\n{\n    static constexpr bool test(T val)\n    {\n        return value_in_range_of_impl2<OfType, T>::test(val);\n    }\n};\n\ntemplate<typename OfType, typename T>\nstruct value_in_range_of_impl1<OfType, T, true>\n{\n    static constexpr bool test(T /*val*/)\n    {\n        return true;\n    }\n};\n\ntemplate<typename OfType, typename T>\ninline constexpr bool value_in_range_of(T val)\n{\n    return value_in_range_of_impl1<OfType, T>::test(val);\n}\n\ntemplate<bool Value>\nusing bool_constant = std::integral_constant<bool, Value>;\n\n///////////////////////////////////////////////////////////////////////////////\n// is_c_string\n///////////////////////////////////////////////////////////////////////////////\n\nnamespace impl\n{\n\ntemplate<typename T>\ninline constexpr bool is_c_string()\n{\n    using TUnExt = typename std::remove_extent<T>::type;\n    using TUnCVExt = typename std::remove_cv<TUnExt>::type;\n    using TUnPtr = typename std::remove_pointer<T>::type;\n    using TUnCVPtr = typename std::remove_cv<TUnPtr>::type;\n    return\n        (std::is_array<T>::value && std::is_same<TUnCVExt, char>::value)\n        || (std::is_pointer<T>::value && std::is_same<TUnCVPtr, char>::value);\n}\n\n}  // namespace impl\n\n// checks whether T is a [cv] char */[cv] char[] C string\ntemplate<typename T>\nstruct is_c_string : bool_constant<impl::is_c_string<T>()> {};\n\ntemplate<typename T>\nusing is_c_string_uncvref = is_c_string<uncvref_t<T>>;\n\n///////////////////////////////////////////////////////////////////////////////\n// is_transparent\n///////////////////////////////////////////////////////////////////////////////\n\nnamespace impl\n{\n\ntemplate<typename T>\ninline constexpr bool is_transparent()\n{\n    return is_detected<detect_is_transparent, T>::value;\n}\n\n}  // namespace impl\n\n// checks whether T has a member named is_transparent\ntemplate<typename T>\nstruct is_transparent : bool_constant<impl::is_transparent<T>()> {};\n\n///////////////////////////////////////////////////////////////////////////////\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/string_concat.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <cstring> // strlen\n#include <string> // string\n#include <utility> // forward\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ninline std::size_t concat_length()\n{\n    return 0;\n}\n\ntemplate<typename... Args>\ninline std::size_t concat_length(const char* cstr, Args&& ... rest);\n\ntemplate<typename StringType, typename... Args>\ninline std::size_t concat_length(const StringType& str, Args&& ... rest);\n\ntemplate<typename... Args>\ninline std::size_t concat_length(const char /*c*/, Args&& ... rest)\n{\n    return 1 + concat_length(std::forward<Args>(rest)...);\n}\n\ntemplate<typename... Args>\ninline std::size_t concat_length(const char* cstr, Args&& ... rest)\n{\n    // cppcheck-suppress ignoredReturnValue\n    return ::strlen(cstr) + concat_length(std::forward<Args>(rest)...);\n}\n\ntemplate<typename StringType, typename... Args>\ninline std::size_t concat_length(const StringType& str, Args&& ... rest)\n{\n    return str.size() + concat_length(std::forward<Args>(rest)...);\n}\n\ntemplate<typename OutStringType>\ninline void concat_into(OutStringType& /*out*/)\n{}\n\ntemplate<typename StringType, typename Arg>\nusing string_can_append = decltype(std::declval<StringType&>().append(std::declval < Arg && > ()));\n\ntemplate<typename StringType, typename Arg>\nusing detect_string_can_append = is_detected<string_can_append, StringType, Arg>;\n\ntemplate<typename StringType, typename Arg>\nusing string_can_append_op = decltype(std::declval<StringType&>() += std::declval < Arg && > ());\n\ntemplate<typename StringType, typename Arg>\nusing detect_string_can_append_op = is_detected<string_can_append_op, StringType, Arg>;\n\ntemplate<typename StringType, typename Arg>\nusing string_can_append_iter = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().begin(), std::declval<const Arg&>().end()));\n\ntemplate<typename StringType, typename Arg>\nusing detect_string_can_append_iter = is_detected<string_can_append_iter, StringType, Arg>;\n\ntemplate<typename StringType, typename Arg>\nusing string_can_append_data = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().data(), std::declval<const Arg&>().size()));\n\ntemplate<typename StringType, typename Arg>\nusing detect_string_can_append_data = is_detected<string_can_append_data, StringType, Arg>;\n\ntemplate < typename OutStringType, typename Arg, typename... Args,\n           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value\n                         && detect_string_can_append_op<OutStringType, Arg>::value, int > = 0 >\ninline void concat_into(OutStringType& out, Arg && arg, Args && ... rest);\n\ntemplate < typename OutStringType, typename Arg, typename... Args,\n           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value\n                         && !detect_string_can_append_op<OutStringType, Arg>::value\n                         && detect_string_can_append_iter<OutStringType, Arg>::value, int > = 0 >\ninline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);\n\ntemplate < typename OutStringType, typename Arg, typename... Args,\n           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value\n                         && !detect_string_can_append_op<OutStringType, Arg>::value\n                         && !detect_string_can_append_iter<OutStringType, Arg>::value\n                         && detect_string_can_append_data<OutStringType, Arg>::value, int > = 0 >\ninline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);\n\ntemplate<typename OutStringType, typename Arg, typename... Args,\n         enable_if_t<detect_string_can_append<OutStringType, Arg>::value, int> = 0>\ninline void concat_into(OutStringType& out, Arg && arg, Args && ... rest)\n{\n    out.append(std::forward<Arg>(arg));\n    concat_into(out, std::forward<Args>(rest)...);\n}\n\ntemplate < typename OutStringType, typename Arg, typename... Args,\n           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value\n                         && detect_string_can_append_op<OutStringType, Arg>::value, int > >\ninline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest)\n{\n    out += std::forward<Arg>(arg);\n    concat_into(out, std::forward<Args>(rest)...);\n}\n\ntemplate < typename OutStringType, typename Arg, typename... Args,\n           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value\n                         && !detect_string_can_append_op<OutStringType, Arg>::value\n                         && detect_string_can_append_iter<OutStringType, Arg>::value, int > >\ninline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)\n{\n    out.append(arg.begin(), arg.end());\n    concat_into(out, std::forward<Args>(rest)...);\n}\n\ntemplate < typename OutStringType, typename Arg, typename... Args,\n           enable_if_t < !detect_string_can_append<OutStringType, Arg>::value\n                         && !detect_string_can_append_op<OutStringType, Arg>::value\n                         && !detect_string_can_append_iter<OutStringType, Arg>::value\n                         && detect_string_can_append_data<OutStringType, Arg>::value, int > >\ninline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)\n{\n    out.append(arg.data(), arg.size());\n    concat_into(out, std::forward<Args>(rest)...);\n}\n\ntemplate<typename OutStringType = std::string, typename... Args>\ninline OutStringType concat(Args && ... args)\n{\n    OutStringType str;\n    str.reserve(concat_length(std::forward<Args>(args)...));\n    concat_into(str, std::forward<Args>(args)...);\n    return str;\n}\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n////////////////\n// exceptions //\n////////////////\n\n/// @brief general exception of the @ref basic_json class\n/// @sa https://json.nlohmann.me/api/basic_json/exception/\nclass exception : public std::exception\n{\n  public:\n    /// returns the explanatory string\n    const char* what() const noexcept override\n    {\n        return m.what();\n    }\n\n    /// the id of the exception\n    const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)\n\n  protected:\n    JSON_HEDLEY_NON_NULL(3)\n    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)\n\n    static std::string name(const std::string& ename, int id_)\n    {\n        return concat(\"[json.exception.\", ename, '.', std::to_string(id_), \"] \");\n    }\n\n    static std::string diagnostics(std::nullptr_t /*leaf_element*/)\n    {\n        return \"\";\n    }\n\n    template<typename BasicJsonType>\n    static std::string diagnostics(const BasicJsonType* leaf_element)\n    {\n#if JSON_DIAGNOSTICS\n        std::vector<std::string> tokens;\n        for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent)\n        {\n            switch (current->m_parent->type())\n            {\n                case value_t::array:\n                {\n                    for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i)\n                    {\n                        if (&current->m_parent->m_value.array->operator[](i) == current)\n                        {\n                            tokens.emplace_back(std::to_string(i));\n                            break;\n                        }\n                    }\n                    break;\n                }\n\n                case value_t::object:\n                {\n                    for (const auto& element : *current->m_parent->m_value.object)\n                    {\n                        if (&element.second == current)\n                        {\n                            tokens.emplace_back(element.first.c_str());\n                            break;\n                        }\n                    }\n                    break;\n                }\n\n                case value_t::null: // LCOV_EXCL_LINE\n                case value_t::string: // LCOV_EXCL_LINE\n                case value_t::boolean: // LCOV_EXCL_LINE\n                case value_t::number_integer: // LCOV_EXCL_LINE\n                case value_t::number_unsigned: // LCOV_EXCL_LINE\n                case value_t::number_float: // LCOV_EXCL_LINE\n                case value_t::binary: // LCOV_EXCL_LINE\n                case value_t::discarded: // LCOV_EXCL_LINE\n                default:   // LCOV_EXCL_LINE\n                    break; // LCOV_EXCL_LINE\n            }\n        }\n\n        if (tokens.empty())\n        {\n            return \"\";\n        }\n\n        auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},\n                                   [](const std::string & a, const std::string & b)\n        {\n            return concat(a, '/', detail::escape(b));\n        });\n        return concat('(', str, \") \");\n#else\n        static_cast<void>(leaf_element);\n        return \"\";\n#endif\n    }\n\n  private:\n    /// an exception object as storage for error messages\n    std::runtime_error m;\n};\n\n/// @brief exception indicating a parse error\n/// @sa https://json.nlohmann.me/api/basic_json/parse_error/\nclass parse_error : public exception\n{\n  public:\n    /*!\n    @brief create a parse error exception\n    @param[in] id_       the id of the exception\n    @param[in] pos       the position where the error occurred (or with\n                         chars_read_total=0 if the position cannot be\n                         determined)\n    @param[in] what_arg  the explanatory string\n    @return parse_error object\n    */\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)\n    {\n        std::string w = concat(exception::name(\"parse_error\", id_), \"parse error\",\n                               position_string(pos), \": \", exception::diagnostics(context), what_arg);\n        return {id_, pos.chars_read_total, w.c_str()};\n    }\n\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)\n    {\n        std::string w = concat(exception::name(\"parse_error\", id_), \"parse error\",\n                               (byte_ != 0 ? (concat(\" at byte \", std::to_string(byte_))) : \"\"),\n                               \": \", exception::diagnostics(context), what_arg);\n        return {id_, byte_, w.c_str()};\n    }\n\n    /*!\n    @brief byte index of the parse error\n\n    The byte index of the last read character in the input file.\n\n    @note For an input with n bytes, 1 is the index of the first character and\n          n+1 is the index of the terminating null byte or the end of file.\n          This also holds true when reading a byte vector (CBOR or MessagePack).\n    */\n    const std::size_t byte;\n\n  private:\n    parse_error(int id_, std::size_t byte_, const char* what_arg)\n        : exception(id_, what_arg), byte(byte_) {}\n\n    static std::string position_string(const position_t& pos)\n    {\n        return concat(\" at line \", std::to_string(pos.lines_read + 1),\n                      \", column \", std::to_string(pos.chars_read_current_line));\n    }\n};\n\n/// @brief exception indicating errors with iterators\n/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/\nclass invalid_iterator : public exception\n{\n  public:\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)\n    {\n        std::string w = concat(exception::name(\"invalid_iterator\", id_), exception::diagnostics(context), what_arg);\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    invalid_iterator(int id_, const char* what_arg)\n        : exception(id_, what_arg) {}\n};\n\n/// @brief exception indicating executing a member function with a wrong type\n/// @sa https://json.nlohmann.me/api/basic_json/type_error/\nclass type_error : public exception\n{\n  public:\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)\n    {\n        std::string w = concat(exception::name(\"type_error\", id_), exception::diagnostics(context), what_arg);\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/// @brief exception indicating access out of the defined range\n/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/\nclass out_of_range : public exception\n{\n  public:\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)\n    {\n        std::string w = concat(exception::name(\"out_of_range\", id_), exception::diagnostics(context), what_arg);\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/// @brief exception indicating other library errors\n/// @sa https://json.nlohmann.me/api/basic_json/other_error/\nclass other_error : public exception\n{\n  public:\n    template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>\n    static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)\n    {\n        std::string w = concat(exception::name(\"other_error\", id_), exception::diagnostics(context), what_arg);\n        return {id_, w.c_str()};\n    }\n\n  private:\n    JSON_HEDLEY_NON_NULL(3)\n    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/identity_tag.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n// dispatching helper struct\ntemplate <class T> struct identity_tag {};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/meta/std_fs.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\n#if JSON_HAS_EXPERIMENTAL_FILESYSTEM\n#include <experimental/filesystem>\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\nnamespace std_fs = std::experimental::filesystem;\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n#elif JSON_HAS_FILESYSTEM\n#include <filesystem>\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\nnamespace std_fs = std::filesystem;\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n#endif\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/string_concat.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_null()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be null, but is \", j.type_name()), &j));\n    }\n    n = nullptr;\n}\n\n// overloads for basic_json template parameters\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t < std::is_arithmetic<ArithmeticType>::value&&\n                         !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n                         int > = 0 >\nvoid get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n\n        case value_t::null:\n        case value_t::object:\n        case value_t::array:\n        case value_t::string:\n        case value_t::boolean:\n        case value_t::binary:\n        case value_t::discarded:\n        default:\n            JSON_THROW(type_error::create(302, concat(\"type must be number, but is \", j.type_name()), &j));\n    }\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be boolean, but is \", j.type_name()), &j));\n    }\n    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be string, but is \", j.type_name()), &j));\n    }\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate <\n    typename BasicJsonType, typename StringType,\n    enable_if_t <\n        std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value\n        && is_detected_exact<typename BasicJsonType::string_t::value_type, value_type_t, StringType>::value\n        && !std::is_same<typename BasicJsonType::string_t, StringType>::value\n        && !is_json_ref<StringType>::value, int > = 0 >\ninline void from_json(const BasicJsonType& j, StringType& s)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be string, but is \", j.type_name()), &j));\n    }\n\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\n#if !JSON_DISABLE_ENUM_SERIALIZATION\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\ninline void from_json(const BasicJsonType& j, EnumType& e)\n{\n    typename std::underlying_type<EnumType>::type val;\n    get_arithmetic_value(j, val);\n    e = static_cast<EnumType>(val);\n}\n#endif  // JSON_DISABLE_ENUM_SERIALIZATION\n\n// forward_list doesn't have an insert method\ntemplate<typename BasicJsonType, typename T, typename Allocator,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\ninline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n    l.clear();\n    std::transform(j.rbegin(), j.rend(),\n                   std::front_inserter(l), [](const BasicJsonType & i)\n    {\n        return i.template get<T>();\n    });\n}\n\n// valarray doesn't have an insert method\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>\ninline void from_json(const BasicJsonType& j, std::valarray<T>& l)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n    l.resize(j.size());\n    std::transform(j.begin(), j.end(), std::begin(l),\n                   [](const BasicJsonType & elem)\n    {\n        return elem.template get<T>();\n    });\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json(const BasicJsonType& j, T (&arr)[N])  // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)\n{\n    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();\n}\n\ntemplate<typename BasicJsonType, typename T, std::size_t N>\nauto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,\n                          priority_tag<2> /*unused*/)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType,\n         enable_if_t<\n             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,\n             int> = 0>\nauto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)\n-> decltype(\n    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),\n    j.template get<typename ConstructibleArrayType::value_type>(),\n    void())\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    ret.reserve(j.size());\n    std::transform(j.begin(), j.end(),\n                   std::inserter(ret, end(ret)), [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType,\n         enable_if_t<\n             std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,\n             int> = 0>\ninline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,\n                                 priority_tag<0> /*unused*/)\n{\n    using std::end;\n\n    ConstructibleArrayType ret;\n    std::transform(\n        j.begin(), j.end(), std::inserter(ret, end(ret)),\n        [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n    arr = std::move(ret);\n}\n\ntemplate < typename BasicJsonType, typename ConstructibleArrayType,\n           enable_if_t <\n               is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&\n               !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&\n               !is_basic_json<ConstructibleArrayType>::value,\n               int > = 0 >\nauto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)\n-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),\nj.template get<typename ConstructibleArrayType::value_type>(),\nvoid())\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n\n    from_json_array_impl(j, arr, priority_tag<3> {});\n}\n\ntemplate < typename BasicJsonType, typename T, std::size_t... Idx >\nstd::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,\n        identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)\n{\n    return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };\n}\n\ntemplate < typename BasicJsonType, typename T, std::size_t N >\nauto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)\n-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n\n    return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});\n}\n\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be binary, but is \", j.type_name()), &j));\n    }\n\n    bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>\ninline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_object()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be object, but is \", j.type_name()), &j));\n    }\n\n    ConstructibleObjectType ret;\n    const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();\n    using value_type = typename ConstructibleObjectType::value_type;\n    std::transform(\n        inner_object->begin(), inner_object->end(),\n        std::inserter(ret, ret.begin()),\n        [](typename BasicJsonType::object_t::value_type const & p)\n    {\n        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());\n    });\n    obj = std::move(ret);\n}\n\n// overload for arithmetic types, not chosen for basic_json template arguments\n// (BooleanType, etc..); note: Is it really necessary to provide explicit\n// overloads for boolean_t etc. in case of a custom BooleanType which is not\n// an arithmetic type?\ntemplate < typename BasicJsonType, typename ArithmeticType,\n           enable_if_t <\n               std::is_arithmetic<ArithmeticType>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&\n               !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n               int > = 0 >\ninline void from_json(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n        case value_t::boolean:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());\n            break;\n        }\n\n        case value_t::null:\n        case value_t::object:\n        case value_t::array:\n        case value_t::string:\n        case value_t::binary:\n        case value_t::discarded:\n        default:\n            JSON_THROW(type_error::create(302, concat(\"type must be number, but is \", j.type_name()), &j));\n    }\n}\n\ntemplate<typename BasicJsonType, typename... Args, std::size_t... Idx>\nstd::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)\n{\n    return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);\n}\n\ntemplate < typename BasicJsonType, class A1, class A2 >\nstd::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)\n{\n    return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),\n            std::forward<BasicJsonType>(j).at(1).template get<A2>()};\n}\n\ntemplate<typename BasicJsonType, typename A1, typename A2>\ninline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)\n{\n    p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nstd::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)\n{\n    return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});\n}\n\ntemplate<typename BasicJsonType, typename... Args>\ninline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)\n{\n    t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});\n}\n\ntemplate<typename BasicJsonType, typename TupleRelated>\nauto from_json(BasicJsonType&& j, TupleRelated&& t)\n-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n\n    return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\ninline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", p.type_name()), &j));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\ntemplate < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,\n           typename = enable_if_t < !std::is_constructible <\n                                        typename BasicJsonType::string_t, Key >::value >>\ninline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", j.type_name()), &j));\n    }\n    m.clear();\n    for (const auto& p : j)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, concat(\"type must be array, but is \", p.type_name()), &j));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\n#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM\ntemplate<typename BasicJsonType>\ninline void from_json(const BasicJsonType& j, std_fs::path& p)\n{\n    if (JSON_HEDLEY_UNLIKELY(!j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, concat(\"type must be string, but is \", j.type_name()), &j));\n    }\n    p = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n#endif\n\nstruct from_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(const BasicJsonType& j, T&& val) const\n    noexcept(noexcept(from_json(j, std::forward<T>(val))))\n    -> decltype(from_json(j, std::forward<T>(val)))\n    {\n        return from_json(j, std::forward<T>(val));\n    }\n};\n\n}  // namespace detail\n\n#ifndef JSON_HAS_CPP_17\n/// namespace to hold default `from_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)\n{\n#endif\nJSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)\n    detail::static_const<detail::from_json_fn>::value;\n#ifndef JSON_HAS_CPP_17\n}  // namespace\n#endif\n\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <algorithm> // copy\n#include <iterator> // begin, end\n#include <string> // string\n#include <tuple> // tuple, get\n#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type\n#include <utility> // move, forward, declval, pair\n#include <valarray> // valarray\n#include <vector> // vector\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <cstddef> // size_t\n#include <iterator> // input_iterator_tag\n#include <string> // string, to_string\n#include <tuple> // tuple_size, get, tuple_element\n#include <utility> // move\n\n#if JSON_HAS_RANGES\n    #include <ranges> // enable_borrowed_range\n#endif\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename string_type>\nvoid int_to_string( string_type& target, std::size_t value )\n{\n    // For ADL\n    using std::to_string;\n    target = to_string(value);\n}\ntemplate<typename IteratorType> class iteration_proxy_value\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    using value_type = iteration_proxy_value;\n    using pointer = value_type *;\n    using reference = value_type &;\n    using iterator_category = std::input_iterator_tag;\n    using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;\n\n  private:\n    /// the iterator\n    IteratorType anchor{};\n    /// an index for arrays (used to create key names)\n    std::size_t array_index = 0;\n    /// last stringified array index\n    mutable std::size_t array_index_last = 0;\n    /// a string representation of the array index\n    mutable string_type array_index_str = \"0\";\n    /// an empty string (to return a reference for primitive values)\n    string_type empty_str{};\n\n  public:\n    explicit iteration_proxy_value() = default;\n    explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)\n    noexcept(std::is_nothrow_move_constructible<IteratorType>::value\n             && std::is_nothrow_default_constructible<string_type>::value)\n        : anchor(std::move(it))\n        , array_index(array_index_)\n    {}\n\n    iteration_proxy_value(iteration_proxy_value const&) = default;\n    iteration_proxy_value& operator=(iteration_proxy_value const&) = default;\n    // older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions\n    iteration_proxy_value(iteration_proxy_value&&)\n    noexcept(std::is_nothrow_move_constructible<IteratorType>::value\n             && std::is_nothrow_move_constructible<string_type>::value) = default;\n    iteration_proxy_value& operator=(iteration_proxy_value&&)\n    noexcept(std::is_nothrow_move_assignable<IteratorType>::value\n             && std::is_nothrow_move_assignable<string_type>::value) = default;\n    ~iteration_proxy_value() = default;\n\n    /// dereference operator (needed for range-based for)\n    const iteration_proxy_value& operator*() const\n    {\n        return *this;\n    }\n\n    /// increment operator (needed for range-based for)\n    iteration_proxy_value& operator++()\n    {\n        ++anchor;\n        ++array_index;\n\n        return *this;\n    }\n\n    iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)\n    {\n        auto tmp = iteration_proxy_value(anchor, array_index);\n        ++anchor;\n        ++array_index;\n        return tmp;\n    }\n\n    /// equality operator (needed for InputIterator)\n    bool operator==(const iteration_proxy_value& o) const\n    {\n        return anchor == o.anchor;\n    }\n\n    /// inequality operator (needed for range-based for)\n    bool operator!=(const iteration_proxy_value& o) const\n    {\n        return anchor != o.anchor;\n    }\n\n    /// return key of the iterator\n    const string_type& key() const\n    {\n        JSON_ASSERT(anchor.m_object != nullptr);\n\n        switch (anchor.m_object->type())\n        {\n            // use integer array index as key\n            case value_t::array:\n            {\n                if (array_index != array_index_last)\n                {\n                    int_to_string( array_index_str, array_index );\n                    array_index_last = array_index;\n                }\n                return array_index_str;\n            }\n\n            // use key from the object\n            case value_t::object:\n                return anchor.key();\n\n            // use an empty key for all primitive types\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return empty_str;\n        }\n    }\n\n    /// return value of the iterator\n    typename IteratorType::reference value() const\n    {\n        return anchor.value();\n    }\n};\n\n/// proxy class for the items() function\ntemplate<typename IteratorType> class iteration_proxy\n{\n  private:\n    /// the container to iterate\n    typename IteratorType::pointer container = nullptr;\n\n  public:\n    explicit iteration_proxy() = default;\n\n    /// construct iteration proxy from a container\n    explicit iteration_proxy(typename IteratorType::reference cont) noexcept\n        : container(&cont) {}\n\n    iteration_proxy(iteration_proxy const&) = default;\n    iteration_proxy& operator=(iteration_proxy const&) = default;\n    iteration_proxy(iteration_proxy&&) noexcept = default;\n    iteration_proxy& operator=(iteration_proxy&&) noexcept = default;\n    ~iteration_proxy() = default;\n\n    /// return iterator begin (needed for range-based for)\n    iteration_proxy_value<IteratorType> begin() const noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container->begin());\n    }\n\n    /// return iterator end (needed for range-based for)\n    iteration_proxy_value<IteratorType> end() const noexcept\n    {\n        return iteration_proxy_value<IteratorType>(container->end());\n    }\n};\n\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())\n{\n    return i.key();\n}\n// Structured Bindings Support\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\ntemplate<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>\nauto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())\n{\n    return i.value();\n}\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// The Addition to the STD Namespace is required to add\n// Structured Bindings Support to the iteration_proxy_value class\n// For further reference see https://blog.tartanllama.xyz/structured-bindings/\n// And see https://github.com/nlohmann/json/pull/1391\nnamespace std\n{\n\n#if defined(__clang__)\n    // Fix: https://github.com/nlohmann/json/issues/1401\n    #pragma clang diagnostic push\n    #pragma clang diagnostic ignored \"-Wmismatched-tags\"\n#endif\ntemplate<typename IteratorType>\nclass tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>\n            : public std::integral_constant<std::size_t, 2> {};\n\ntemplate<std::size_t N, typename IteratorType>\nclass tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>\n{\n  public:\n    using type = decltype(\n                     get<N>(std::declval <\n                            ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));\n};\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n\n}  // namespace std\n\n#if JSON_HAS_RANGES\n    template <typename IteratorType>\n    inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;\n#endif\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/std_fs.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n//////////////////\n// constructors //\n//////////////////\n\n/*\n * Note all external_constructor<>::construct functions need to call\n * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an\n * allocated value (e.g., a string). See bug issue\n * https://github.com/nlohmann/json/issues/2865 for more information.\n */\n\ntemplate<value_t> struct external_constructor;\n\ntemplate<>\nstruct external_constructor<value_t::boolean>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::boolean;\n        j.m_value = b;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::string>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::string;\n        j.m_value = s;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::string;\n        j.m_value = std::move(s);\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleStringType,\n               enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleStringType& str)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::string;\n        j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::binary>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::binary;\n        j.m_value = typename BasicJsonType::binary_t(b);\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::binary;\n        j.m_value = typename BasicJsonType::binary_t(std::move(b));\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_float>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::number_float;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_unsigned>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::number_unsigned;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_integer>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::number_integer;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::array>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = arr;\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = std::move(arr);\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleArrayType,\n               enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,\n                             int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const std::vector<bool>& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->reserve(arr.size());\n        for (const bool x : arr)\n        {\n            j.m_value.array->push_back(x);\n            j.set_parent(j.m_value.array->back());\n        }\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename T,\n             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\n    static void construct(BasicJsonType& j, const std::valarray<T>& arr)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->resize(arr.size());\n        if (arr.size() > 0)\n        {\n            std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());\n        }\n        j.set_parents();\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::object>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::object;\n        j.m_value = obj;\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n    {\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::object;\n        j.m_value = std::move(obj);\n        j.set_parents();\n        j.assert_invariant();\n    }\n\n    template < typename BasicJsonType, typename CompatibleObjectType,\n               enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >\n    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_value.destroy(j.m_type);\n        j.m_type = value_t::object;\n        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));\n        j.set_parents();\n        j.assert_invariant();\n    }\n};\n\n/////////////\n// to_json //\n/////////////\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>\ninline void to_json(BasicJsonType& j, T b) noexcept\n{\n    external_constructor<value_t::boolean>::construct(j, b);\n}\n\ntemplate < typename BasicJsonType, typename BoolRef,\n           enable_if_t <\n               ((std::is_same<std::vector<bool>::reference, BoolRef>::value\n                 && !std::is_same <std::vector<bool>::reference, typename BasicJsonType::boolean_t&>::value)\n                || (std::is_same<std::vector<bool>::const_reference, BoolRef>::value\n                    && !std::is_same <detail::uncvref_t<std::vector<bool>::const_reference>,\n                                      typename BasicJsonType::boolean_t >::value))\n               && std::is_convertible<const BoolRef&, typename BasicJsonType::boolean_t>::value, int > = 0 >\ninline void to_json(BasicJsonType& j, const BoolRef& b) noexcept\n{\n    external_constructor<value_t::boolean>::construct(j, static_cast<typename BasicJsonType::boolean_t>(b));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleString,\n         enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>\ninline void to_json(BasicJsonType& j, const CompatibleString& s)\n{\n    external_constructor<value_t::string>::construct(j, s);\n}\n\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n{\n    external_constructor<value_t::string>::construct(j, std::move(s));\n}\n\ntemplate<typename BasicJsonType, typename FloatType,\n         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>\ninline void to_json(BasicJsonType& j, FloatType val) noexcept\n{\n    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberUnsignedType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>\ninline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept\n{\n    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberIntegerType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>\ninline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept\n{\n    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));\n}\n\n#if !JSON_DISABLE_ENUM_SERIALIZATION\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\ninline void to_json(BasicJsonType& j, EnumType e) noexcept\n{\n    using underlying_type = typename std::underlying_type<EnumType>::type;\n    external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));\n}\n#endif  // JSON_DISABLE_ENUM_SERIALIZATION\n\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, const std::vector<bool>& e)\n{\n    external_constructor<value_t::array>::construct(j, e);\n}\n\ntemplate < typename BasicJsonType, typename CompatibleArrayType,\n           enable_if_t < is_compatible_array_type<BasicJsonType,\n                         CompatibleArrayType>::value&&\n                         !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&\n                         !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&\n                         !is_basic_json<CompatibleArrayType>::value,\n                         int > = 0 >\ninline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)\n{\n    external_constructor<value_t::binary>::construct(j, bin);\n}\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\ninline void to_json(BasicJsonType& j, const std::valarray<T>& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate < typename BasicJsonType, typename CompatibleObjectType,\n           enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >\ninline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)\n{\n    external_constructor<value_t::object>::construct(j, obj);\n}\n\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n{\n    external_constructor<value_t::object>::construct(j, std::move(obj));\n}\n\ntemplate <\n    typename BasicJsonType, typename T, std::size_t N,\n    enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,\n                  const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n                  int > = 0 >\ninline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >\ninline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)\n{\n    j = { p.first, p.second };\n}\n\n// for https://github.com/nlohmann/json/pull/1134\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>\ninline void to_json(BasicJsonType& j, const T& b)\n{\n    j = { {b.key(), b.value()} };\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\ninline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    j = { std::get<Idx>(t)... };\n}\n\ntemplate<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>\ninline void to_json(BasicJsonType& j, const T& t)\n{\n    to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});\n}\n\n#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM\ntemplate<typename BasicJsonType>\ninline void to_json(BasicJsonType& j, const std_fs::path& p)\n{\n    j = p.string();\n}\n#endif\n\nstruct to_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))\n    -> decltype(to_json(j, std::forward<T>(val)), void())\n    {\n        return to_json(j, std::forward<T>(val));\n    }\n};\n}  // namespace detail\n\n#ifndef JSON_HAS_CPP_17\n/// namespace to hold default `to_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)\n{\n#endif\nJSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)\n    detail::static_const<detail::to_json_fn>::value;\n#ifndef JSON_HAS_CPP_17\n}  // namespace\n#endif\n\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/meta/identity_tag.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/// @sa https://json.nlohmann.me/api/adl_serializer/\ntemplate<typename ValueType, typename>\nstruct adl_serializer\n{\n    /// @brief convert a JSON value to any value type\n    /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto from_json(BasicJsonType && j, TargetType& val) noexcept(\n        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())\n    {\n        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);\n    }\n\n    /// @brief convert a JSON value to any value type\n    /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto from_json(BasicJsonType && j) noexcept(\n    noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))\n    {\n        return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});\n    }\n\n    /// @brief convert any value type to a JSON value\n    /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/\n    template<typename BasicJsonType, typename TargetType = ValueType>\n    static auto to_json(BasicJsonType& j, TargetType && val) noexcept(\n        noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))\n    -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())\n    {\n        ::nlohmann::to_json(j, std::forward<TargetType>(val));\n    }\n};\n\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/byte_container_with_subtype.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <cstdint> // uint8_t, uint64_t\n#include <tuple> // tie\n#include <utility> // move\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/// @brief an internal type for a backed binary type\n/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/\ntemplate<typename BinaryType>\nclass byte_container_with_subtype : public BinaryType\n{\n  public:\n    using container_type = BinaryType;\n    using subtype_type = std::uint64_t;\n\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/\n    byte_container_with_subtype() noexcept(noexcept(container_type()))\n        : container_type()\n    {}\n\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/\n    byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n    {}\n\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/\n    byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n    {}\n\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/\n    byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b)))\n        : container_type(b)\n        , m_subtype(subtype_)\n        , m_has_subtype(true)\n    {}\n\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/\n    byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b))))\n        : container_type(std::move(b))\n        , m_subtype(subtype_)\n        , m_has_subtype(true)\n    {}\n\n    bool operator==(const byte_container_with_subtype& rhs) const\n    {\n        return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==\n               std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);\n    }\n\n    bool operator!=(const byte_container_with_subtype& rhs) const\n    {\n        return !(rhs == *this);\n    }\n\n    /// @brief sets the binary subtype\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/\n    void set_subtype(subtype_type subtype_) noexcept\n    {\n        m_subtype = subtype_;\n        m_has_subtype = true;\n    }\n\n    /// @brief return the binary subtype\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/\n    constexpr subtype_type subtype() const noexcept\n    {\n        return m_has_subtype ? m_subtype : static_cast<subtype_type>(-1);\n    }\n\n    /// @brief return whether the value has a subtype\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/\n    constexpr bool has_subtype() const noexcept\n    {\n        return m_has_subtype;\n    }\n\n    /// @brief clears the binary subtype\n    /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/\n    void clear_subtype() noexcept\n    {\n        m_subtype = 0;\n        m_has_subtype = false;\n    }\n\n  private:\n    subtype_type m_subtype = 0;\n    bool m_has_subtype = false;\n};\n\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/hash.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <cstdint> // uint8_t\n#include <cstddef> // size_t\n#include <functional> // hash\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n// boost::hash_combine\ninline std::size_t combine(std::size_t seed, std::size_t h) noexcept\n{\n    seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);\n    return seed;\n}\n\n/*!\n@brief hash a JSON value\n\nThe hash function tries to rely on std::hash where possible. Furthermore, the\ntype of the JSON value is taken into account to have different hash values for\nnull, 0, 0U, and false, etc.\n\n@tparam BasicJsonType basic_json specialization\n@param j JSON value to hash\n@return hash value of j\n*/\ntemplate<typename BasicJsonType>\nstd::size_t hash(const BasicJsonType& j)\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n\n    const auto type = static_cast<std::size_t>(j.type());\n    switch (j.type())\n    {\n        case BasicJsonType::value_t::null:\n        case BasicJsonType::value_t::discarded:\n        {\n            return combine(type, 0);\n        }\n\n        case BasicJsonType::value_t::object:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j.items())\n            {\n                const auto h = std::hash<string_t> {}(element.key());\n                seed = combine(seed, h);\n                seed = combine(seed, hash(element.value()));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::array:\n        {\n            auto seed = combine(type, j.size());\n            for (const auto& element : j)\n            {\n                seed = combine(seed, hash(element));\n            }\n            return seed;\n        }\n\n        case BasicJsonType::value_t::string:\n        {\n            const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::boolean:\n        {\n            const auto h = std::hash<bool> {}(j.template get<bool>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_integer:\n        {\n            const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_unsigned:\n        {\n            const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::number_float:\n        {\n            const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());\n            return combine(type, h);\n        }\n\n        case BasicJsonType::value_t::binary:\n        {\n            auto seed = combine(type, j.get_binary().size());\n            const auto h = std::hash<bool> {}(j.get_binary().has_subtype());\n            seed = combine(seed, h);\n            seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype()));\n            for (const auto byte : j.get_binary())\n            {\n                seed = combine(seed, std::hash<std::uint8_t> {}(byte));\n            }\n            return seed;\n        }\n\n        default:                   // LCOV_EXCL_LINE\n            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            return 0;              // LCOV_EXCL_LINE\n    }\n}\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <algorithm> // generate_n\n#include <array> // array\n#include <cmath> // ldexp\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstdio> // snprintf\n#include <cstring> // memcpy\n#include <iterator> // back_inserter\n#include <limits> // numeric_limits\n#include <string> // char_traits, string\n#include <utility> // make_pair, move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <array> // array\n#include <cstddef> // size_t\n#include <cstring> // strlen\n#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next\n#include <memory> // shared_ptr, make_shared, addressof\n#include <numeric> // accumulate\n#include <string> // string, char_traits\n#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer\n#include <utility> // pair, declval\n\n#ifndef JSON_NO_IO\n    #include <cstdio>   // FILE *\n    #include <istream>  // istream\n#endif                  // JSON_NO_IO\n\n// #include <nlohmann/detail/iterators/iterator_traits.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/// the supported input formats\nenum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };\n\n////////////////////\n// input adapters //\n////////////////////\n\n#ifndef JSON_NO_IO\n/*!\nInput adapter for stdio file access. This adapter read only 1 byte and do not use any\n buffer. This adapter is a very low level adapter.\n*/\nclass file_input_adapter\n{\n  public:\n    using char_type = char;\n\n    JSON_HEDLEY_NON_NULL(2)\n    explicit file_input_adapter(std::FILE* f) noexcept\n        : m_file(f)\n    {\n        JSON_ASSERT(m_file != nullptr);\n    }\n\n    // make class move-only\n    file_input_adapter(const file_input_adapter&) = delete;\n    file_input_adapter(file_input_adapter&&) noexcept = default;\n    file_input_adapter& operator=(const file_input_adapter&) = delete;\n    file_input_adapter& operator=(file_input_adapter&&) = delete;\n    ~file_input_adapter() = default;\n\n    std::char_traits<char>::int_type get_character() noexcept\n    {\n        return std::fgetc(m_file);\n    }\n\n  private:\n    /// the file pointer to read from\n    std::FILE* m_file;\n};\n\n\n/*!\nInput adapter for a (caching) istream. Ignores a UFT Byte Order Mark at\nbeginning of input. Does not support changing the underlying std::streambuf\nin mid-input. Maintains underlying std::istream and std::streambuf to support\nsubsequent use of standard std::istream operations to process any input\ncharacters following those used in parsing the JSON input.  Clears the\nstd::istream flags; any input errors (e.g., EOF) will be detected by the first\nsubsequent call for input from the std::istream.\n*/\nclass input_stream_adapter\n{\n  public:\n    using char_type = char;\n\n    ~input_stream_adapter()\n    {\n        // clear stream flags; we use underlying streambuf I/O, do not\n        // maintain ifstream flags, except eof\n        if (is != nullptr)\n        {\n            is->clear(is->rdstate() & std::ios::eofbit);\n        }\n    }\n\n    explicit input_stream_adapter(std::istream& i)\n        : is(&i), sb(i.rdbuf())\n    {}\n\n    // delete because of pointer members\n    input_stream_adapter(const input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&&) = delete;\n\n    input_stream_adapter(input_stream_adapter&& rhs) noexcept\n        : is(rhs.is), sb(rhs.sb)\n    {\n        rhs.is = nullptr;\n        rhs.sb = nullptr;\n    }\n\n    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to\n    // ensure that std::char_traits<char>::eof() and the character 0xFF do not\n    // end up as the same value, e.g. 0xFFFFFFFF.\n    std::char_traits<char>::int_type get_character()\n    {\n        auto res = sb->sbumpc();\n        // set eof manually, as we don't use the istream interface.\n        if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))\n        {\n            is->clear(is->rdstate() | std::ios::eofbit);\n        }\n        return res;\n    }\n\n  private:\n    /// the associated input stream\n    std::istream* is = nullptr;\n    std::streambuf* sb = nullptr;\n};\n#endif  // JSON_NO_IO\n\n// General-purpose iterator-based adapter. It might not be as fast as\n// theoretically possible for some containers, but it is extremely versatile.\ntemplate<typename IteratorType>\nclass iterator_input_adapter\n{\n  public:\n    using char_type = typename std::iterator_traits<IteratorType>::value_type;\n\n    iterator_input_adapter(IteratorType first, IteratorType last)\n        : current(std::move(first)), end(std::move(last))\n    {}\n\n    typename std::char_traits<char_type>::int_type get_character()\n    {\n        if (JSON_HEDLEY_LIKELY(current != end))\n        {\n            auto result = std::char_traits<char_type>::to_int_type(*current);\n            std::advance(current, 1);\n            return result;\n        }\n\n        return std::char_traits<char_type>::eof();\n    }\n\n  private:\n    IteratorType current;\n    IteratorType end;\n\n    template<typename BaseInputAdapter, size_t T>\n    friend struct wide_string_input_helper;\n\n    bool empty() const\n    {\n        return current == end;\n    }\n};\n\n\ntemplate<typename BaseInputAdapter, size_t T>\nstruct wide_string_input_helper;\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 4>\n{\n    // UTF-32\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-32 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (wc <= 0xFFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else if (wc <= 0x10FFFF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 4;\n            }\n            else\n            {\n                // unknown character\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n        }\n    }\n};\n\ntemplate<typename BaseInputAdapter>\nstruct wide_string_input_helper<BaseInputAdapter, 2>\n{\n    // UTF-16\n    static void fill_buffer(BaseInputAdapter& input,\n                            std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,\n                            size_t& utf8_bytes_index,\n                            size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (JSON_HEDLEY_UNLIKELY(input.empty()))\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = input.get_character();\n\n            // UTF-16 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 2;\n            }\n            else if (0xD800 > wc || wc >= 0xE000)\n            {\n                utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));\n                utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));\n                utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));\n                utf8_bytes_filled = 3;\n            }\n            else\n            {\n                if (JSON_HEDLEY_UNLIKELY(!input.empty()))\n                {\n                    const auto wc2 = static_cast<unsigned int>(input.get_character());\n                    const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));\n                    utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));\n                    utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));\n                    utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));\n                    utf8_bytes_filled = 4;\n                }\n                else\n                {\n                    utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);\n                    utf8_bytes_filled = 1;\n                }\n            }\n        }\n    }\n};\n\n// Wraps another input apdater to convert wide character types into individual bytes.\ntemplate<typename BaseInputAdapter, typename WideCharType>\nclass wide_string_input_adapter\n{\n  public:\n    using char_type = char;\n\n    wide_string_input_adapter(BaseInputAdapter base)\n        : base_adapter(base) {}\n\n    typename std::char_traits<char>::int_type get_character() noexcept\n    {\n        // check if buffer needs to be filled\n        if (utf8_bytes_index == utf8_bytes_filled)\n        {\n            fill_buffer<sizeof(WideCharType)>();\n\n            JSON_ASSERT(utf8_bytes_filled > 0);\n            JSON_ASSERT(utf8_bytes_index == 0);\n        }\n\n        // use buffer\n        JSON_ASSERT(utf8_bytes_filled > 0);\n        JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);\n        return utf8_bytes[utf8_bytes_index++];\n    }\n\n  private:\n    BaseInputAdapter base_adapter;\n\n    template<size_t T>\n    void fill_buffer()\n    {\n        wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);\n    }\n\n    /// a buffer for UTF-8 bytes\n    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};\n\n    /// index to the utf8_codes array for the next valid byte\n    std::size_t utf8_bytes_index = 0;\n    /// number of valid bytes in the utf8_codes array\n    std::size_t utf8_bytes_filled = 0;\n};\n\n\ntemplate<typename IteratorType, typename Enable = void>\nstruct iterator_input_adapter_factory\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using adapter_type = iterator_input_adapter<iterator_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(std::move(first), std::move(last));\n    }\n};\n\ntemplate<typename T>\nstruct is_iterator_of_multibyte\n{\n    using value_type = typename std::iterator_traits<T>::value_type;\n    enum\n    {\n        value = sizeof(value_type) > 1\n    };\n};\n\ntemplate<typename IteratorType>\nstruct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>\n{\n    using iterator_type = IteratorType;\n    using char_type = typename std::iterator_traits<iterator_type>::value_type;\n    using base_adapter_type = iterator_input_adapter<iterator_type>;\n    using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;\n\n    static adapter_type create(IteratorType first, IteratorType last)\n    {\n        return adapter_type(base_adapter_type(std::move(first), std::move(last)));\n    }\n};\n\n// General purpose iterator-based input\ntemplate<typename IteratorType>\ntypename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)\n{\n    using factory_type = iterator_input_adapter_factory<IteratorType>;\n    return factory_type::create(first, last);\n}\n\n// Convenience shorthand from container to iterator\n// Enables ADL on begin(container) and end(container)\n// Encloses the using declarations in namespace for not to leak them to outside scope\n\nnamespace container_input_adapter_factory_impl\n{\n\nusing std::begin;\nusing std::end;\n\ntemplate<typename ContainerType, typename Enable = void>\nstruct container_input_adapter_factory {};\n\ntemplate<typename ContainerType>\nstruct container_input_adapter_factory< ContainerType,\n       void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>\n       {\n           using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));\n\n           static adapter_type create(const ContainerType& container)\n{\n    return input_adapter(begin(container), end(container));\n}\n       };\n\n}  // namespace container_input_adapter_factory_impl\n\ntemplate<typename ContainerType>\ntypename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)\n{\n    return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);\n}\n\n#ifndef JSON_NO_IO\n// Special cases with fast paths\ninline file_input_adapter input_adapter(std::FILE* file)\n{\n    return file_input_adapter(file);\n}\n\ninline input_stream_adapter input_adapter(std::istream& stream)\n{\n    return input_stream_adapter(stream);\n}\n\ninline input_stream_adapter input_adapter(std::istream&& stream)\n{\n    return input_stream_adapter(stream);\n}\n#endif  // JSON_NO_IO\n\nusing contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));\n\n// Null-delimited strings, and the like.\ntemplate < typename CharT,\n           typename std::enable_if <\n               std::is_pointer<CharT>::value&&\n               !std::is_array<CharT>::value&&\n               std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n               sizeof(typename std::remove_pointer<CharT>::type) == 1,\n               int >::type = 0 >\ncontiguous_bytes_input_adapter input_adapter(CharT b)\n{\n    auto length = std::strlen(reinterpret_cast<const char*>(b));\n    const auto* ptr = reinterpret_cast<const char*>(b);\n    return input_adapter(ptr, ptr + length);\n}\n\ntemplate<typename T, std::size_t N>\nauto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n{\n    return input_adapter(array, array + N);\n}\n\n// This class only handles inputs of input_buffer_adapter type.\n// It's required so that expressions like {ptr, len} can be implicitly cast\n// to the correct adapter.\nclass span_input_adapter\n{\n  public:\n    template < typename CharT,\n               typename std::enable_if <\n                   std::is_pointer<CharT>::value&&\n                   std::is_integral<typename std::remove_pointer<CharT>::type>::value&&\n                   sizeof(typename std::remove_pointer<CharT>::type) == 1,\n                   int >::type = 0 >\n    span_input_adapter(CharT b, std::size_t l)\n        : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}\n\n    template<class IteratorType,\n             typename std::enable_if<\n                 std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,\n                 int>::type = 0>\n    span_input_adapter(IteratorType first, IteratorType last)\n        : ia(input_adapter(first, last)) {}\n\n    contiguous_bytes_input_adapter&& get()\n    {\n        return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)\n    }\n\n  private:\n    contiguous_bytes_input_adapter ia;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <cstddef>\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/string_concat.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/*!\n@brief SAX interface\n\nThis class describes the SAX interface used by @ref nlohmann::json::sax_parse.\nEach function is called in different situations while the input is parsed. The\nboolean return value informs the parser whether to continue processing the\ninput.\n*/\ntemplate<typename BasicJsonType>\nstruct json_sax\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    /*!\n    @brief a null value was read\n    @return whether parsing should proceed\n    */\n    virtual bool null() = 0;\n\n    /*!\n    @brief a boolean value was read\n    @param[in] val  boolean value\n    @return whether parsing should proceed\n    */\n    virtual bool boolean(bool val) = 0;\n\n    /*!\n    @brief an integer number was read\n    @param[in] val  integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_integer(number_integer_t val) = 0;\n\n    /*!\n    @brief an unsigned integer number was read\n    @param[in] val  unsigned integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_unsigned(number_unsigned_t val) = 0;\n\n    /*!\n    @brief a floating-point number was read\n    @param[in] val  floating-point value\n    @param[in] s    raw token value\n    @return whether parsing should proceed\n    */\n    virtual bool number_float(number_float_t val, const string_t& s) = 0;\n\n    /*!\n    @brief a string value was read\n    @param[in] val  string value\n    @return whether parsing should proceed\n    @note It is safe to move the passed string value.\n    */\n    virtual bool string(string_t& val) = 0;\n\n    /*!\n    @brief a binary value was read\n    @param[in] val  binary value\n    @return whether parsing should proceed\n    @note It is safe to move the passed binary value.\n    */\n    virtual bool binary(binary_t& val) = 0;\n\n    /*!\n    @brief the beginning of an object was read\n    @param[in] elements  number of object elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_object(std::size_t elements) = 0;\n\n    /*!\n    @brief an object key was read\n    @param[in] val  object key\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool key(string_t& val) = 0;\n\n    /*!\n    @brief the end of an object was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_object() = 0;\n\n    /*!\n    @brief the beginning of an array was read\n    @param[in] elements  number of array elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_array(std::size_t elements) = 0;\n\n    /*!\n    @brief the end of an array was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_array() = 0;\n\n    /*!\n    @brief a parse error occurred\n    @param[in] position    the position in the input where the error occurs\n    @param[in] last_token  the last read token\n    @param[in] ex          an exception object describing the error\n    @return whether parsing should proceed (must return false)\n    */\n    virtual bool parse_error(std::size_t position,\n                             const std::string& last_token,\n                             const detail::exception& ex) = 0;\n\n    json_sax() = default;\n    json_sax(const json_sax&) = default;\n    json_sax(json_sax&&) noexcept = default;\n    json_sax& operator=(const json_sax&) = default;\n    json_sax& operator=(json_sax&&) noexcept = default;\n    virtual ~json_sax() = default;\n};\n\n\nnamespace detail\n{\n/*!\n@brief SAX implementation to create a JSON value from SAX events\n\nThis class implements the @ref json_sax interface and processes the SAX events\nto create a JSON value which makes it basically a DOM parser. The structure or\nhierarchy of the JSON value is managed by the stack `ref_stack` which contains\na pointer to the respective array or object for each recursion depth.\n\nAfter successful parsing, the value that is passed by reference to the\nconstructor contains the parsed value.\n\n@tparam BasicJsonType  the JSON type\n*/\ntemplate<typename BasicJsonType>\nclass json_sax_dom_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    /*!\n    @param[in,out] r  reference to a JSON value that is manipulated while\n                       parsing\n    @param[in] allow_exceptions_  whether parse errors yield exceptions\n    */\n    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)\n        : root(r), allow_exceptions(allow_exceptions_)\n    {}\n\n    // make class move-only\n    json_sax_dom_parser(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;\n    json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~json_sax_dom_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));\n\n        if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, concat(\"excessive object size: \", std::to_string(len)), ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(ref_stack.back()->is_object());\n\n        // add null at given key and store the reference for later\n        object_element = &(ref_stack.back()->m_value.object->operator[](val));\n        return true;\n    }\n\n    bool end_object()\n    {\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(ref_stack.back()->is_object());\n\n        ref_stack.back()->set_parents();\n        ref_stack.pop_back();\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));\n\n        if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, concat(\"excessive array size: \", std::to_string(len)), ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(ref_stack.back()->is_array());\n\n        ref_stack.back()->set_parents();\n        ref_stack.pop_back();\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n    */\n    template<typename Value>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    BasicJsonType* handle_value(Value&& v)\n    {\n        if (ref_stack.empty())\n        {\n            root = BasicJsonType(std::forward<Value>(v));\n            return &root;\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));\n            return &(ref_stack.back()->m_value.array->back());\n        }\n\n        JSON_ASSERT(ref_stack.back()->is_object());\n        JSON_ASSERT(object_element);\n        *object_element = BasicJsonType(std::forward<Value>(v));\n        return object_element;\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_dom_callback_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using parser_callback_t = typename BasicJsonType::parser_callback_t;\n    using parse_event_t = typename BasicJsonType::parse_event_t;\n\n    json_sax_dom_callback_parser(BasicJsonType& r,\n                                 const parser_callback_t cb,\n                                 const bool allow_exceptions_ = true)\n        : root(r), callback(cb), allow_exceptions(allow_exceptions_)\n    {\n        keep_stack.push_back(true);\n    }\n\n    // make class move-only\n    json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;\n    json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~json_sax_dom_callback_parser() = default;\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool binary(binary_t& val)\n    {\n        handle_value(std::move(val));\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        // check callback for object start\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::object, true);\n        ref_stack.push_back(val.second);\n\n        // check object limit\n        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, concat(\"excessive object size: \", std::to_string(len)), ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        BasicJsonType k = BasicJsonType(val);\n\n        // check callback for key\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);\n        key_keep_stack.push_back(keep);\n\n        // add discarded value at given key and store the reference for later\n        if (keep && ref_stack.back())\n        {\n            object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);\n        }\n\n        return true;\n    }\n\n    bool end_object()\n    {\n        if (ref_stack.back())\n        {\n            if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))\n            {\n                // discard object\n                *ref_stack.back() = discarded;\n            }\n            else\n            {\n                ref_stack.back()->set_parents();\n            }\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())\n        {\n            // remove discarded value\n            for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)\n            {\n                if (it->is_discarded())\n                {\n                    ref_stack.back()->erase(it);\n                    break;\n                }\n            }\n        }\n\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::array, true);\n        ref_stack.push_back(val.second);\n\n        // check array limit\n        if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408, concat(\"excessive array size: \", std::to_string(len)), ref_stack.back()));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        bool keep = true;\n\n        if (ref_stack.back())\n        {\n            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());\n            if (keep)\n            {\n                ref_stack.back()->set_parents();\n            }\n            else\n            {\n                // discard array\n                *ref_stack.back() = discarded;\n            }\n        }\n\n        JSON_ASSERT(!ref_stack.empty());\n        JSON_ASSERT(!keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        // remove discarded value\n        if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->pop_back();\n        }\n\n        return true;\n    }\n\n    template<class Exception>\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const Exception& ex)\n    {\n        errored = true;\n        static_cast<void>(ex);\n        if (allow_exceptions)\n        {\n            JSON_THROW(ex);\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @param[in] v  value to add to the JSON value we build during parsing\n    @param[in] skip_callback  whether we should skip calling the callback\n               function; this is required after start_array() and\n               start_object() SAX events, because otherwise we would call the\n               callback function with an empty array or object, respectively.\n\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n\n    @return pair of boolean (whether value should be kept) and pointer (to the\n            passed value in the ref_stack hierarchy; nullptr if not kept)\n    */\n    template<typename Value>\n    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)\n    {\n        JSON_ASSERT(!keep_stack.empty());\n\n        // do not handle this value if we know it would be added to a discarded\n        // container\n        if (!keep_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // create value\n        auto value = BasicJsonType(std::forward<Value>(v));\n\n        // check callback\n        const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);\n\n        // do not handle this value if we just learnt it shall be discarded\n        if (!keep)\n        {\n            return {false, nullptr};\n        }\n\n        if (ref_stack.empty())\n        {\n            root = std::move(value);\n            return {true, &root};\n        }\n\n        // skip this value if we already decided to skip the parent\n        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)\n        if (!ref_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // we now only expect arrays and objects\n        JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());\n\n        // array\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->emplace_back(std::move(value));\n            return {true, &(ref_stack.back()->m_value.array->back())};\n        }\n\n        // object\n        JSON_ASSERT(ref_stack.back()->is_object());\n        // check if we should store an element for the current key\n        JSON_ASSERT(!key_keep_stack.empty());\n        const bool store_element = key_keep_stack.back();\n        key_keep_stack.pop_back();\n\n        if (!store_element)\n        {\n            return {false, nullptr};\n        }\n\n        JSON_ASSERT(object_element);\n        *object_element = std::move(value);\n        return {true, object_element};\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack {};\n    /// stack to manage which values to keep\n    std::vector<bool> keep_stack {};\n    /// stack to manage which object keys to keep\n    std::vector<bool> key_keep_stack {};\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// callback function\n    const parser_callback_t callback = nullptr;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n    /// a discarded value for the callback\n    BasicJsonType discarded = BasicJsonType::value_t::discarded;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_acceptor\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n\n    bool null()\n    {\n        return true;\n    }\n\n    bool boolean(bool /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_integer(number_integer_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool string(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool binary(binary_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool start_object(std::size_t /*unused*/ = static_cast<std::size_t>(-1))\n    {\n        return true;\n    }\n\n    bool key(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool end_object()\n    {\n        return true;\n    }\n\n    bool start_array(std::size_t /*unused*/ = static_cast<std::size_t>(-1))\n    {\n        return true;\n    }\n\n    bool end_array()\n    {\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)\n    {\n        return false;\n    }\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/input/lexer.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <array> // array\n#include <clocale> // localeconv\n#include <cstddef> // size_t\n#include <cstdio> // snprintf\n#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull\n#include <initializer_list> // initializer_list\n#include <string> // char_traits, string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/position_t.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n///////////\n// lexer //\n///////////\n\ntemplate<typename BasicJsonType>\nclass lexer_base\n{\n  public:\n    /// token types for the parser\n    enum class token_type\n    {\n        uninitialized,    ///< indicating the scanner is uninitialized\n        literal_true,     ///< the `true` literal\n        literal_false,    ///< the `false` literal\n        literal_null,     ///< the `null` literal\n        value_string,     ///< a string -- use get_string() for actual value\n        value_unsigned,   ///< an unsigned integer -- use get_number_unsigned() for actual value\n        value_integer,    ///< a signed integer -- use get_number_integer() for actual value\n        value_float,      ///< an floating point number -- use get_number_float() for actual value\n        begin_array,      ///< the character for array begin `[`\n        begin_object,     ///< the character for object begin `{`\n        end_array,        ///< the character for array end `]`\n        end_object,       ///< the character for object end `}`\n        name_separator,   ///< the name separator `:`\n        value_separator,  ///< the value separator `,`\n        parse_error,      ///< indicating a parse error\n        end_of_input,     ///< indicating the end of the input buffer\n        literal_or_value  ///< a literal or the begin of a value (only for diagnostics)\n    };\n\n    /// return name of values of type token_type (only used for errors)\n    JSON_HEDLEY_RETURNS_NON_NULL\n    JSON_HEDLEY_CONST\n    static const char* token_type_name(const token_type t) noexcept\n    {\n        switch (t)\n        {\n            case token_type::uninitialized:\n                return \"<uninitialized>\";\n            case token_type::literal_true:\n                return \"true literal\";\n            case token_type::literal_false:\n                return \"false literal\";\n            case token_type::literal_null:\n                return \"null literal\";\n            case token_type::value_string:\n                return \"string literal\";\n            case token_type::value_unsigned:\n            case token_type::value_integer:\n            case token_type::value_float:\n                return \"number literal\";\n            case token_type::begin_array:\n                return \"'['\";\n            case token_type::begin_object:\n                return \"'{'\";\n            case token_type::end_array:\n                return \"']'\";\n            case token_type::end_object:\n                return \"'}'\";\n            case token_type::name_separator:\n                return \"':'\";\n            case token_type::value_separator:\n                return \"','\";\n            case token_type::parse_error:\n                return \"<parse error>\";\n            case token_type::end_of_input:\n                return \"end of input\";\n            case token_type::literal_or_value:\n                return \"'[', '{', or a literal\";\n            // LCOV_EXCL_START\n            default: // catch non-enum values\n                return \"unknown token\";\n                // LCOV_EXCL_STOP\n        }\n    }\n};\n/*!\n@brief lexical analysis\n\nThis class organizes the lexical analysis during JSON deserialization.\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass lexer : public lexer_base<BasicJsonType>\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename std::char_traits<char_type>::int_type;\n\n  public:\n    using token_type = typename lexer_base<BasicJsonType>::token_type;\n\n    explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept\n        : ia(std::move(adapter))\n        , ignore_comments(ignore_comments_)\n        , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))\n    {}\n\n    // delete because of pointer members\n    lexer(const lexer&) = delete;\n    lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    lexer& operator=(lexer&) = delete;\n    lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~lexer() = default;\n\n  private:\n    /////////////////////\n    // locales\n    /////////////////////\n\n    /// return the locale-dependent decimal point\n    JSON_HEDLEY_PURE\n    static char get_decimal_point() noexcept\n    {\n        const auto* loc = localeconv();\n        JSON_ASSERT(loc != nullptr);\n        return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);\n    }\n\n    /////////////////////\n    // scan functions\n    /////////////////////\n\n    /*!\n    @brief get codepoint from 4 hex characters following `\\u`\n\n    For input \"\\u c1 c2 c3 c4\" the codepoint is:\n      (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4\n    = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)\n\n    Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'\n    must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The\n    conversion is done by subtracting the offset (0x30, 0x37, and 0x57)\n    between the ASCII value of the character and the desired integer value.\n\n    @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or\n            non-hex character)\n    */\n    int get_codepoint()\n    {\n        // this function only makes sense after reading `\\u`\n        JSON_ASSERT(current == 'u');\n        int codepoint = 0;\n\n        const auto factors = { 12u, 8u, 4u, 0u };\n        for (const auto factor : factors)\n        {\n            get();\n\n            if (current >= '0' && current <= '9')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);\n            }\n            else if (current >= 'A' && current <= 'F')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);\n            }\n            else if (current >= 'a' && current <= 'f')\n            {\n                codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);\n            }\n            else\n            {\n                return -1;\n            }\n        }\n\n        JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);\n        return codepoint;\n    }\n\n    /*!\n    @brief check if the next byte(s) are inside a given range\n\n    Adds the current byte and, for each passed range, reads a new byte and\n    checks if it is inside the range. If a violation was detected, set up an\n    error message and return false. Otherwise, return true.\n\n    @param[in] ranges  list of integers; interpreted as list of pairs of\n                       inclusive lower and upper bound, respectively\n\n    @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,\n         1, 2, or 3 pairs. This precondition is enforced by an assertion.\n\n    @return true if and only if no range violation was detected\n    */\n    bool next_byte_in_range(std::initializer_list<char_int_type> ranges)\n    {\n        JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);\n        add(current);\n\n        for (auto range = ranges.begin(); range != ranges.end(); ++range)\n        {\n            get();\n            if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range)))\n            {\n                add(current);\n            }\n            else\n            {\n                error_message = \"invalid string: ill-formed UTF-8 byte\";\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief scan a string literal\n\n    This function scans a string according to Sect. 7 of RFC 8259. While\n    scanning, bytes are escaped and copied into buffer token_buffer. Then the\n    function returns successfully, token_buffer is *not* null-terminated (as it\n    may contain \\0 bytes), and token_buffer.size() is the number of bytes in the\n    string.\n\n    @return token_type::value_string if string could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note In case of errors, variable error_message contains a textual\n          description.\n    */\n    token_type scan_string()\n    {\n        // reset token_buffer (ignore opening quote)\n        reset();\n\n        // we entered the function by reading an open quote\n        JSON_ASSERT(current == '\\\"');\n\n        while (true)\n        {\n            // get next character\n            switch (get())\n            {\n                // end of file while parsing string\n                case std::char_traits<char_type>::eof():\n                {\n                    error_message = \"invalid string: missing closing quote\";\n                    return token_type::parse_error;\n                }\n\n                // closing quote\n                case '\\\"':\n                {\n                    return token_type::value_string;\n                }\n\n                // escapes\n                case '\\\\':\n                {\n                    switch (get())\n                    {\n                        // quotation mark\n                        case '\\\"':\n                            add('\\\"');\n                            break;\n                        // reverse solidus\n                        case '\\\\':\n                            add('\\\\');\n                            break;\n                        // solidus\n                        case '/':\n                            add('/');\n                            break;\n                        // backspace\n                        case 'b':\n                            add('\\b');\n                            break;\n                        // form feed\n                        case 'f':\n                            add('\\f');\n                            break;\n                        // line feed\n                        case 'n':\n                            add('\\n');\n                            break;\n                        // carriage return\n                        case 'r':\n                            add('\\r');\n                            break;\n                        // tab\n                        case 't':\n                            add('\\t');\n                            break;\n\n                        // unicode escapes\n                        case 'u':\n                        {\n                            const int codepoint1 = get_codepoint();\n                            int codepoint = codepoint1; // start with codepoint1\n\n                            if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))\n                            {\n                                error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                return token_type::parse_error;\n                            }\n\n                            // check if code point is a high surrogate\n                            if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)\n                            {\n                                // expect next \\uxxxx entry\n                                if (JSON_HEDLEY_LIKELY(get() == '\\\\' && get() == 'u'))\n                                {\n                                    const int codepoint2 = get_codepoint();\n\n                                    if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))\n                                    {\n                                        error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                        return token_type::parse_error;\n                                    }\n\n                                    // check if codepoint2 is a low surrogate\n                                    if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))\n                                    {\n                                        // overwrite codepoint\n                                        codepoint = static_cast<int>(\n                                                        // high surrogate occupies the most significant 22 bits\n                                                        (static_cast<unsigned int>(codepoint1) << 10u)\n                                                        // low surrogate occupies the least significant 15 bits\n                                                        + static_cast<unsigned int>(codepoint2)\n                                                        // there is still the 0xD800, 0xDC00 and 0x10000 noise\n                                                        // in the result, so we have to subtract with:\n                                                        // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00\n                                                        - 0x35FDC00u);\n                                    }\n                                    else\n                                    {\n                                        error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                        return token_type::parse_error;\n                                    }\n                                }\n                                else\n                                {\n                                    error_message = \"invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n                            else\n                            {\n                                if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))\n                                {\n                                    error_message = \"invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n\n                            // result of the above calculation yields a proper codepoint\n                            JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);\n\n                            // translate codepoint into bytes\n                            if (codepoint < 0x80)\n                            {\n                                // 1-byte characters: 0xxxxxxx (ASCII)\n                                add(static_cast<char_int_type>(codepoint));\n                            }\n                            else if (codepoint <= 0x7FF)\n                            {\n                                // 2-byte characters: 110xxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else if (codepoint <= 0xFFFF)\n                            {\n                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n                            else\n                            {\n                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n                                add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));\n                                add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));\n                            }\n\n                            break;\n                        }\n\n                        // other characters after escape\n                        default:\n                            error_message = \"invalid string: forbidden character after backslash\";\n                            return token_type::parse_error;\n                    }\n\n                    break;\n                }\n\n                // invalid control characters\n                case 0x00:\n                {\n                    error_message = \"invalid string: control character U+0000 (NUL) must be escaped to \\\\u0000\";\n                    return token_type::parse_error;\n                }\n\n                case 0x01:\n                {\n                    error_message = \"invalid string: control character U+0001 (SOH) must be escaped to \\\\u0001\";\n                    return token_type::parse_error;\n                }\n\n                case 0x02:\n                {\n                    error_message = \"invalid string: control character U+0002 (STX) must be escaped to \\\\u0002\";\n                    return token_type::parse_error;\n                }\n\n                case 0x03:\n                {\n                    error_message = \"invalid string: control character U+0003 (ETX) must be escaped to \\\\u0003\";\n                    return token_type::parse_error;\n                }\n\n                case 0x04:\n                {\n                    error_message = \"invalid string: control character U+0004 (EOT) must be escaped to \\\\u0004\";\n                    return token_type::parse_error;\n                }\n\n                case 0x05:\n                {\n                    error_message = \"invalid string: control character U+0005 (ENQ) must be escaped to \\\\u0005\";\n                    return token_type::parse_error;\n                }\n\n                case 0x06:\n                {\n                    error_message = \"invalid string: control character U+0006 (ACK) must be escaped to \\\\u0006\";\n                    return token_type::parse_error;\n                }\n\n                case 0x07:\n                {\n                    error_message = \"invalid string: control character U+0007 (BEL) must be escaped to \\\\u0007\";\n                    return token_type::parse_error;\n                }\n\n                case 0x08:\n                {\n                    error_message = \"invalid string: control character U+0008 (BS) must be escaped to \\\\u0008 or \\\\b\";\n                    return token_type::parse_error;\n                }\n\n                case 0x09:\n                {\n                    error_message = \"invalid string: control character U+0009 (HT) must be escaped to \\\\u0009 or \\\\t\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0A:\n                {\n                    error_message = \"invalid string: control character U+000A (LF) must be escaped to \\\\u000A or \\\\n\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0B:\n                {\n                    error_message = \"invalid string: control character U+000B (VT) must be escaped to \\\\u000B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0C:\n                {\n                    error_message = \"invalid string: control character U+000C (FF) must be escaped to \\\\u000C or \\\\f\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0D:\n                {\n                    error_message = \"invalid string: control character U+000D (CR) must be escaped to \\\\u000D or \\\\r\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0E:\n                {\n                    error_message = \"invalid string: control character U+000E (SO) must be escaped to \\\\u000E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0F:\n                {\n                    error_message = \"invalid string: control character U+000F (SI) must be escaped to \\\\u000F\";\n                    return token_type::parse_error;\n                }\n\n                case 0x10:\n                {\n                    error_message = \"invalid string: control character U+0010 (DLE) must be escaped to \\\\u0010\";\n                    return token_type::parse_error;\n                }\n\n                case 0x11:\n                {\n                    error_message = \"invalid string: control character U+0011 (DC1) must be escaped to \\\\u0011\";\n                    return token_type::parse_error;\n                }\n\n                case 0x12:\n                {\n                    error_message = \"invalid string: control character U+0012 (DC2) must be escaped to \\\\u0012\";\n                    return token_type::parse_error;\n                }\n\n                case 0x13:\n                {\n                    error_message = \"invalid string: control character U+0013 (DC3) must be escaped to \\\\u0013\";\n                    return token_type::parse_error;\n                }\n\n                case 0x14:\n                {\n                    error_message = \"invalid string: control character U+0014 (DC4) must be escaped to \\\\u0014\";\n                    return token_type::parse_error;\n                }\n\n                case 0x15:\n                {\n                    error_message = \"invalid string: control character U+0015 (NAK) must be escaped to \\\\u0015\";\n                    return token_type::parse_error;\n                }\n\n                case 0x16:\n                {\n                    error_message = \"invalid string: control character U+0016 (SYN) must be escaped to \\\\u0016\";\n                    return token_type::parse_error;\n                }\n\n                case 0x17:\n                {\n                    error_message = \"invalid string: control character U+0017 (ETB) must be escaped to \\\\u0017\";\n                    return token_type::parse_error;\n                }\n\n                case 0x18:\n                {\n                    error_message = \"invalid string: control character U+0018 (CAN) must be escaped to \\\\u0018\";\n                    return token_type::parse_error;\n                }\n\n                case 0x19:\n                {\n                    error_message = \"invalid string: control character U+0019 (EM) must be escaped to \\\\u0019\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1A:\n                {\n                    error_message = \"invalid string: control character U+001A (SUB) must be escaped to \\\\u001A\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1B:\n                {\n                    error_message = \"invalid string: control character U+001B (ESC) must be escaped to \\\\u001B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1C:\n                {\n                    error_message = \"invalid string: control character U+001C (FS) must be escaped to \\\\u001C\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1D:\n                {\n                    error_message = \"invalid string: control character U+001D (GS) must be escaped to \\\\u001D\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1E:\n                {\n                    error_message = \"invalid string: control character U+001E (RS) must be escaped to \\\\u001E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1F:\n                {\n                    error_message = \"invalid string: control character U+001F (US) must be escaped to \\\\u001F\";\n                    return token_type::parse_error;\n                }\n\n                // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))\n                case 0x20:\n                case 0x21:\n                case 0x23:\n                case 0x24:\n                case 0x25:\n                case 0x26:\n                case 0x27:\n                case 0x28:\n                case 0x29:\n                case 0x2A:\n                case 0x2B:\n                case 0x2C:\n                case 0x2D:\n                case 0x2E:\n                case 0x2F:\n                case 0x30:\n                case 0x31:\n                case 0x32:\n                case 0x33:\n                case 0x34:\n                case 0x35:\n                case 0x36:\n                case 0x37:\n                case 0x38:\n                case 0x39:\n                case 0x3A:\n                case 0x3B:\n                case 0x3C:\n                case 0x3D:\n                case 0x3E:\n                case 0x3F:\n                case 0x40:\n                case 0x41:\n                case 0x42:\n                case 0x43:\n                case 0x44:\n                case 0x45:\n                case 0x46:\n                case 0x47:\n                case 0x48:\n                case 0x49:\n                case 0x4A:\n                case 0x4B:\n                case 0x4C:\n                case 0x4D:\n                case 0x4E:\n                case 0x4F:\n                case 0x50:\n                case 0x51:\n                case 0x52:\n                case 0x53:\n                case 0x54:\n                case 0x55:\n                case 0x56:\n                case 0x57:\n                case 0x58:\n                case 0x59:\n                case 0x5A:\n                case 0x5B:\n                case 0x5D:\n                case 0x5E:\n                case 0x5F:\n                case 0x60:\n                case 0x61:\n                case 0x62:\n                case 0x63:\n                case 0x64:\n                case 0x65:\n                case 0x66:\n                case 0x67:\n                case 0x68:\n                case 0x69:\n                case 0x6A:\n                case 0x6B:\n                case 0x6C:\n                case 0x6D:\n                case 0x6E:\n                case 0x6F:\n                case 0x70:\n                case 0x71:\n                case 0x72:\n                case 0x73:\n                case 0x74:\n                case 0x75:\n                case 0x76:\n                case 0x77:\n                case 0x78:\n                case 0x79:\n                case 0x7A:\n                case 0x7B:\n                case 0x7C:\n                case 0x7D:\n                case 0x7E:\n                case 0x7F:\n                {\n                    add(current);\n                    break;\n                }\n\n                // U+0080..U+07FF: bytes C2..DF 80..BF\n                case 0xC2:\n                case 0xC3:\n                case 0xC4:\n                case 0xC5:\n                case 0xC6:\n                case 0xC7:\n                case 0xC8:\n                case 0xC9:\n                case 0xCA:\n                case 0xCB:\n                case 0xCC:\n                case 0xCD:\n                case 0xCE:\n                case 0xCF:\n                case 0xD0:\n                case 0xD1:\n                case 0xD2:\n                case 0xD3:\n                case 0xD4:\n                case 0xD5:\n                case 0xD6:\n                case 0xD7:\n                case 0xD8:\n                case 0xD9:\n                case 0xDA:\n                case 0xDB:\n                case 0xDC:\n                case 0xDD:\n                case 0xDE:\n                case 0xDF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+0800..U+0FFF: bytes E0 A0..BF 80..BF\n                case 0xE0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF\n                // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF\n                case 0xE1:\n                case 0xE2:\n                case 0xE3:\n                case 0xE4:\n                case 0xE5:\n                case 0xE6:\n                case 0xE7:\n                case 0xE8:\n                case 0xE9:\n                case 0xEA:\n                case 0xEB:\n                case 0xEC:\n                case 0xEE:\n                case 0xEF:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+D000..U+D7FF: bytes ED 80..9F 80..BF\n                case 0xED:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF\n                case 0xF0:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF\n                case 0xF1:\n                case 0xF2:\n                case 0xF3:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF\n                case 0xF4:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // remaining bytes (80..C1 and F5..FF) are ill-formed\n                default:\n                {\n                    error_message = \"invalid string: ill-formed UTF-8 byte\";\n                    return token_type::parse_error;\n                }\n            }\n        }\n    }\n\n    /*!\n     * @brief scan a comment\n     * @return whether comment could be scanned successfully\n     */\n    bool scan_comment()\n    {\n        switch (get())\n        {\n            // single-line comments skip input until a newline or EOF is read\n            case '/':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case '\\n':\n                        case '\\r':\n                        case std::char_traits<char_type>::eof():\n                        case '\\0':\n                            return true;\n\n                        default:\n                            break;\n                    }\n                }\n            }\n\n            // multi-line comments skip input until */ is read\n            case '*':\n            {\n                while (true)\n                {\n                    switch (get())\n                    {\n                        case std::char_traits<char_type>::eof():\n                        case '\\0':\n                        {\n                            error_message = \"invalid comment; missing closing '*/'\";\n                            return false;\n                        }\n\n                        case '*':\n                        {\n                            switch (get())\n                            {\n                                case '/':\n                                    return true;\n\n                                default:\n                                {\n                                    unget();\n                                    continue;\n                                }\n                            }\n                        }\n\n                        default:\n                            continue;\n                    }\n                }\n            }\n\n            // unexpected character after reading '/'\n            default:\n            {\n                error_message = \"invalid comment; expecting '/' or '*' after '/'\";\n                return false;\n            }\n        }\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(float& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtof(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtod(str, endptr);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    static void strtof(long double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtold(str, endptr);\n    }\n\n    /*!\n    @brief scan a number literal\n\n    This function scans a string according to Sect. 6 of RFC 8259.\n\n    The function is realized with a deterministic finite state machine derived\n    from the grammar described in RFC 8259. Starting in state \"init\", the\n    input is read and used to determined the next state. Only state \"done\"\n    accepts the number. State \"error\" is a trap state to model errors. In the\n    table below, \"anything\" means any character but the ones listed before.\n\n    state    | 0        | 1-9      | e E      | +       | -       | .        | anything\n    ---------|----------|----------|----------|---------|---------|----------|-----------\n    init     | zero     | any1     | [error]  | [error] | minus   | [error]  | [error]\n    minus    | zero     | any1     | [error]  | [error] | [error] | [error]  | [error]\n    zero     | done     | done     | exponent | done    | done    | decimal1 | done\n    any1     | any1     | any1     | exponent | done    | done    | decimal1 | done\n    decimal1 | decimal2 | decimal2 | [error]  | [error] | [error] | [error]  | [error]\n    decimal2 | decimal2 | decimal2 | exponent | done    | done    | done     | done\n    exponent | any2     | any2     | [error]  | sign    | sign    | [error]  | [error]\n    sign     | any2     | any2     | [error]  | [error] | [error] | [error]  | [error]\n    any2     | any2     | any2     | done     | done    | done    | done     | done\n\n    The state machine is realized with one label per state (prefixed with\n    \"scan_number_\") and `goto` statements between them. The state machine\n    contains cycles, but any cycle can be left when EOF is read. Therefore,\n    the function is guaranteed to terminate.\n\n    During scanning, the read bytes are stored in token_buffer. This string is\n    then converted to a signed integer, an unsigned integer, or a\n    floating-point number.\n\n    @return token_type::value_unsigned, token_type::value_integer, or\n            token_type::value_float if number could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note The scanner is independent of the current locale. Internally, the\n          locale's decimal point is used instead of `.` to work with the\n          locale-dependent converters.\n    */\n    token_type scan_number()  // lgtm [cpp/use-of-goto]\n    {\n        // reset token_buffer to store the number's bytes\n        reset();\n\n        // the type of the parsed number; initially set to unsigned; will be\n        // changed if minus sign, decimal point or exponent is read\n        token_type number_type = token_type::value_unsigned;\n\n        // state (init): we just found out we need to scan a number\n        switch (current)\n        {\n            case '-':\n            {\n                add(current);\n                goto scan_number_minus;\n            }\n\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            // all other characters are rejected outside scan_number()\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\nscan_number_minus:\n        // state: we just parsed a leading minus sign\n        number_type = token_type::value_integer;\n        switch (get())\n        {\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '-'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_zero:\n        // state: we just parse a zero (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_any1:\n        // state: we just parsed a number 0-9 (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_decimal1:\n        // state: we just parsed a decimal point\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '.'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_decimal2:\n        // we just parsed at least one number after a decimal point\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_exponent:\n        // we just parsed an exponent\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '+':\n            case '-':\n            {\n                add(current);\n                goto scan_number_sign;\n            }\n\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message =\n                    \"invalid number; expected '+', '-', or digit after exponent\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_sign:\n        // we just parsed an exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after exponent sign\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_any2:\n        // we just parsed a number after the exponent or exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_done:\n        // unget the character after the number (we only read it to know that\n        // we are done scanning a number)\n        unget();\n\n        char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        errno = 0;\n\n        // try to parse integers first and fall back to floats\n        if (number_type == token_type::value_unsigned)\n        {\n            const auto x = std::strtoull(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_unsigned = static_cast<number_unsigned_t>(x);\n                if (value_unsigned == x)\n                {\n                    return token_type::value_unsigned;\n                }\n            }\n        }\n        else if (number_type == token_type::value_integer)\n        {\n            const auto x = std::strtoll(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_integer = static_cast<number_integer_t>(x);\n                if (value_integer == x)\n                {\n                    return token_type::value_integer;\n                }\n            }\n        }\n\n        // this code is reached if we parse a floating-point number or if an\n        // integer conversion above failed\n        strtof(value_float, token_buffer.data(), &endptr);\n\n        // we checked the number format before\n        JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());\n\n        return token_type::value_float;\n    }\n\n    /*!\n    @param[in] literal_text  the literal text to expect\n    @param[in] length        the length of the passed literal text\n    @param[in] return_type   the token type to return on success\n    */\n    JSON_HEDLEY_NON_NULL(2)\n    token_type scan_literal(const char_type* literal_text, const std::size_t length,\n                            token_type return_type)\n    {\n        JSON_ASSERT(std::char_traits<char_type>::to_char_type(current) == literal_text[0]);\n        for (std::size_t i = 1; i < length; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(std::char_traits<char_type>::to_char_type(get()) != literal_text[i]))\n            {\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n            }\n        }\n        return return_type;\n    }\n\n    /////////////////////\n    // input management\n    /////////////////////\n\n    /// reset token_buffer; current character is beginning of token\n    void reset() noexcept\n    {\n        token_buffer.clear();\n        token_string.clear();\n        token_string.push_back(std::char_traits<char_type>::to_char_type(current));\n    }\n\n    /*\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a\n    `std::char_traits<char>::eof()` in that case.  Stores the scanned characters\n    for use in error messages.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++position.chars_read_total;\n        ++position.chars_read_current_line;\n\n        if (next_unget)\n        {\n            // just reset the next_unget variable and work with current\n            next_unget = false;\n        }\n        else\n        {\n            current = ia.get_character();\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))\n        {\n            token_string.push_back(std::char_traits<char_type>::to_char_type(current));\n        }\n\n        if (current == '\\n')\n        {\n            ++position.lines_read;\n            position.chars_read_current_line = 0;\n        }\n\n        return current;\n    }\n\n    /*!\n    @brief unget current character (read it again on next get)\n\n    We implement unget by setting variable next_unget to true. The input is not\n    changed - we just simulate ungetting by modifying chars_read_total,\n    chars_read_current_line, and token_string. The next call to get() will\n    behave as if the unget character is read again.\n    */\n    void unget()\n    {\n        next_unget = true;\n\n        --position.chars_read_total;\n\n        // in case we \"unget\" a newline, we have to also decrement the lines_read\n        if (position.chars_read_current_line == 0)\n        {\n            if (position.lines_read > 0)\n            {\n                --position.lines_read;\n            }\n        }\n        else\n        {\n            --position.chars_read_current_line;\n        }\n\n        if (JSON_HEDLEY_LIKELY(current != std::char_traits<char_type>::eof()))\n        {\n            JSON_ASSERT(!token_string.empty());\n            token_string.pop_back();\n        }\n    }\n\n    /// add a character to token_buffer\n    void add(char_int_type c)\n    {\n        token_buffer.push_back(static_cast<typename string_t::value_type>(c));\n    }\n\n  public:\n    /////////////////////\n    // value getters\n    /////////////////////\n\n    /// return integer value\n    constexpr number_integer_t get_number_integer() const noexcept\n    {\n        return value_integer;\n    }\n\n    /// return unsigned integer value\n    constexpr number_unsigned_t get_number_unsigned() const noexcept\n    {\n        return value_unsigned;\n    }\n\n    /// return floating-point value\n    constexpr number_float_t get_number_float() const noexcept\n    {\n        return value_float;\n    }\n\n    /// return current string value (implicitly resets the token; useful only once)\n    string_t& get_string()\n    {\n        return token_buffer;\n    }\n\n    /////////////////////\n    // diagnostics\n    /////////////////////\n\n    /// return position of last read token\n    constexpr position_t get_position() const noexcept\n    {\n        return position;\n    }\n\n    /// return the last read token (for errors only).  Will never contain EOF\n    /// (an arbitrary value that is not a valid char value, often -1), because\n    /// 255 may legitimately occur.  May contain NUL, which should be escaped.\n    std::string get_token_string() const\n    {\n        // escape control characters\n        std::string result;\n        for (const auto c : token_string)\n        {\n            if (static_cast<unsigned char>(c) <= '\\x1F')\n            {\n                // escape control characters\n                std::array<char, 9> cs{{}};\n                static_cast<void>((std::snprintf)(cs.data(), cs.size(), \"<U+%.4X>\", static_cast<unsigned char>(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                result += cs.data();\n            }\n            else\n            {\n                // add character as is\n                result.push_back(static_cast<std::string::value_type>(c));\n            }\n        }\n\n        return result;\n    }\n\n    /// return syntax error message\n    JSON_HEDLEY_RETURNS_NON_NULL\n    constexpr const char* get_error_message() const noexcept\n    {\n        return error_message;\n    }\n\n    /////////////////////\n    // actual scanner\n    /////////////////////\n\n    /*!\n    @brief skip the UTF-8 byte order mark\n    @return true iff there is no BOM or the correct BOM has been skipped\n    */\n    bool skip_bom()\n    {\n        if (get() == 0xEF)\n        {\n            // check if we completely parse the BOM\n            return get() == 0xBB && get() == 0xBF;\n        }\n\n        // the first character is not the beginning of the BOM; unget it to\n        // process is later\n        unget();\n        return true;\n    }\n\n    void skip_whitespace()\n    {\n        do\n        {\n            get();\n        }\n        while (current == ' ' || current == '\\t' || current == '\\n' || current == '\\r');\n    }\n\n    token_type scan()\n    {\n        // initially, skip the BOM\n        if (position.chars_read_total == 0 && !skip_bom())\n        {\n            error_message = \"invalid BOM; must be 0xEF 0xBB 0xBF if given\";\n            return token_type::parse_error;\n        }\n\n        // read next character and ignore whitespace\n        skip_whitespace();\n\n        // ignore comments\n        while (ignore_comments && current == '/')\n        {\n            if (!scan_comment())\n            {\n                return token_type::parse_error;\n            }\n\n            // skip following whitespace\n            skip_whitespace();\n        }\n\n        switch (current)\n        {\n            // structural characters\n            case '[':\n                return token_type::begin_array;\n            case ']':\n                return token_type::end_array;\n            case '{':\n                return token_type::begin_object;\n            case '}':\n                return token_type::end_object;\n            case ':':\n                return token_type::name_separator;\n            case ',':\n                return token_type::value_separator;\n\n            // literals\n            case 't':\n            {\n                std::array<char_type, 4> true_literal = {{static_cast<char_type>('t'), static_cast<char_type>('r'), static_cast<char_type>('u'), static_cast<char_type>('e')}};\n                return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);\n            }\n            case 'f':\n            {\n                std::array<char_type, 5> false_literal = {{static_cast<char_type>('f'), static_cast<char_type>('a'), static_cast<char_type>('l'), static_cast<char_type>('s'), static_cast<char_type>('e')}};\n                return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);\n            }\n            case 'n':\n            {\n                std::array<char_type, 4> null_literal = {{static_cast<char_type>('n'), static_cast<char_type>('u'), static_cast<char_type>('l'), static_cast<char_type>('l')}};\n                return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);\n            }\n\n            // string\n            case '\\\"':\n                return scan_string();\n\n            // number\n            case '-':\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n                return scan_number();\n\n            // end of input (the null byte is needed when parsing from\n            // string literals)\n            case '\\0':\n            case std::char_traits<char_type>::eof():\n                return token_type::end_of_input;\n\n            // error\n            default:\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n        }\n    }\n\n  private:\n    /// input adapter\n    InputAdapterType ia;\n\n    /// whether comments should be ignored (true) or signaled as errors (false)\n    const bool ignore_comments = false;\n\n    /// the current character\n    char_int_type current = std::char_traits<char_type>::eof();\n\n    /// whether the next get() call should just return current\n    bool next_unget = false;\n\n    /// the start position of the current token\n    position_t position {};\n\n    /// raw input token string (for error messages)\n    std::vector<char_type> token_string {};\n\n    /// buffer for variable-length tokens (numbers, strings)\n    string_t token_buffer {};\n\n    /// a description of occurred lexer errors\n    const char* error_message = \"\";\n\n    // number values\n    number_integer_t value_integer = 0;\n    number_unsigned_t value_unsigned = 0;\n    number_float_t value_float = 0;\n\n    /// the decimal point\n    const char_int_type decimal_point_char = '.';\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <cstdint> // size_t\n#include <utility> // declval\n#include <string> // string\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename T>\nusing null_function_t = decltype(std::declval<T&>().null());\n\ntemplate<typename T>\nusing boolean_function_t =\n    decltype(std::declval<T&>().boolean(std::declval<bool>()));\n\ntemplate<typename T, typename Integer>\nusing number_integer_function_t =\n    decltype(std::declval<T&>().number_integer(std::declval<Integer>()));\n\ntemplate<typename T, typename Unsigned>\nusing number_unsigned_function_t =\n    decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));\n\ntemplate<typename T, typename Float, typename String>\nusing number_float_function_t = decltype(std::declval<T&>().number_float(\n                                    std::declval<Float>(), std::declval<const String&>()));\n\ntemplate<typename T, typename String>\nusing string_function_t =\n    decltype(std::declval<T&>().string(std::declval<String&>()));\n\ntemplate<typename T, typename Binary>\nusing binary_function_t =\n    decltype(std::declval<T&>().binary(std::declval<Binary&>()));\n\ntemplate<typename T>\nusing start_object_function_t =\n    decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));\n\ntemplate<typename T, typename String>\nusing key_function_t =\n    decltype(std::declval<T&>().key(std::declval<String&>()));\n\ntemplate<typename T>\nusing end_object_function_t = decltype(std::declval<T&>().end_object());\n\ntemplate<typename T>\nusing start_array_function_t =\n    decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));\n\ntemplate<typename T>\nusing end_array_function_t = decltype(std::declval<T&>().end_array());\n\ntemplate<typename T, typename Exception>\nusing parse_error_function_t = decltype(std::declval<T&>().parse_error(\n        std::declval<std::size_t>(), std::declval<const std::string&>(),\n        std::declval<const Exception&>()));\n\ntemplate<typename SAX, typename BasicJsonType>\nstruct is_sax\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static constexpr bool value =\n        is_detected_exact<bool, null_function_t, SAX>::value &&\n        is_detected_exact<bool, boolean_function_t, SAX>::value &&\n        is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&\n        is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&\n        is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&\n        is_detected_exact<bool, start_object_function_t, SAX>::value &&\n        is_detected_exact<bool, key_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, end_object_function_t, SAX>::value &&\n        is_detected_exact<bool, start_array_function_t, SAX>::value &&\n        is_detected_exact<bool, end_array_function_t, SAX>::value &&\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;\n};\n\ntemplate<typename SAX, typename BasicJsonType>\nstruct is_sax_static_asserts\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static_assert(is_detected_exact<bool, null_function_t, SAX>::value,\n                  \"Missing/invalid function: bool null()\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(\n        is_detected_exact<bool, number_integer_function_t, SAX,\n        number_integer_t>::value,\n        \"Missing/invalid function: bool number_integer(number_integer_t)\");\n    static_assert(\n        is_detected_exact<bool, number_unsigned_function_t, SAX,\n        number_unsigned_t>::value,\n        \"Missing/invalid function: bool number_unsigned(number_unsigned_t)\");\n    static_assert(is_detected_exact<bool, number_float_function_t, SAX,\n                  number_float_t, string_t>::value,\n                  \"Missing/invalid function: bool number_float(number_float_t, const string_t&)\");\n    static_assert(\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value,\n        \"Missing/invalid function: bool string(string_t&)\");\n    static_assert(\n        is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,\n        \"Missing/invalid function: bool binary(binary_t&)\");\n    static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_object(std::size_t)\");\n    static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,\n                  \"Missing/invalid function: bool key(string_t&)\");\n    static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_object()\");\n    static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_array(std::size_t)\");\n    static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_array()\");\n    static_assert(\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,\n        \"Missing/invalid function: bool parse_error(std::size_t, const \"\n        \"std::string&, const exception&)\");\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/string_concat.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/// how to treat CBOR tags\nenum class cbor_tag_handler_t\n{\n    error,   ///< throw a parse_error exception in case of a tag\n    ignore,  ///< ignore tags\n    store    ///< store tags as binary type\n};\n\n/*!\n@brief determine system byte order\n\n@return true if and only if system's byte order is little endian\n\n@note from https://stackoverflow.com/a/1001328/266378\n*/\nstatic inline bool little_endianness(int num = 1) noexcept\n{\n    return *reinterpret_cast<char*>(&num) == 1;\n}\n\n\n///////////////////\n// binary reader //\n///////////////////\n\n/*!\n@brief deserialization of CBOR, MessagePack, and UBJSON values\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType>>\nclass binary_reader\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using json_sax_t = SAX;\n    using char_type = typename InputAdapterType::char_type;\n    using char_int_type = typename std::char_traits<char_type>::int_type;\n\n  public:\n    /*!\n    @brief create a binary reader\n\n    @param[in] adapter  input adapter to read from\n    */\n    explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json) noexcept : ia(std::move(adapter)), input_format(format)\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n    }\n\n    // make class move-only\n    binary_reader(const binary_reader&) = delete;\n    binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    binary_reader& operator=(const binary_reader&) = delete;\n    binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)\n    ~binary_reader() = default;\n\n    /*!\n    @param[in] format  the binary format to parse\n    @param[in] sax_    a SAX event processor\n    @param[in] strict  whether to expect the input to be consumed completed\n    @param[in] tag_handler  how to treat CBOR tags\n\n    @return whether parsing was successful\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool sax_parse(const input_format_t format,\n                   json_sax_t* sax_,\n                   const bool strict = true,\n                   const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        sax = sax_;\n        bool result = false;\n\n        switch (format)\n        {\n            case input_format_t::bson:\n                result = parse_bson_internal();\n                break;\n\n            case input_format_t::cbor:\n                result = parse_cbor_internal(true, tag_handler);\n                break;\n\n            case input_format_t::msgpack:\n                result = parse_msgpack_internal();\n                break;\n\n            case input_format_t::ubjson:\n            case input_format_t::bjdata:\n                result = parse_ubjson_internal();\n                break;\n\n            case input_format_t::json: // LCOV_EXCL_LINE\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\n        // strict mode: next byte must be EOF\n        if (result && strict)\n        {\n            if (input_format == input_format_t::ubjson || input_format == input_format_t::bjdata)\n            {\n                get_ignore_noop();\n            }\n            else\n            {\n                get();\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))\n            {\n                return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read,\n                                        exception_message(input_format, concat(\"expected end of input; last byte: 0x\", get_token_string()), \"value\"), nullptr));\n            }\n        }\n\n        return result;\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @brief Reads in a BSON-object and passes it to the SAX-parser.\n    @return whether a valid BSON-value was passed to the SAX parser\n    */\n    bool parse_bson_internal()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))\n        {\n            return false;\n        }\n\n        return sax->end_object();\n    }\n\n    /*!\n    @brief Parses a C-style string from the BSON input.\n    @param[in,out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @return `true` if the \\x00-byte indicating the end of the string was\n             encountered before the EOF; false` indicates an unexpected EOF.\n    */\n    bool get_bson_cstr(string_t& result)\n    {\n        auto out = std::back_inserter(result);\n        while (true)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"cstring\")))\n            {\n                return false;\n            }\n            if (current == 0x00)\n            {\n                return true;\n            }\n            *out++ = static_cast<typename string_t::value_type>(current);\n        }\n    }\n\n    /*!\n    @brief Parses a zero-terminated string of length @a len from the BSON\n           input.\n    @param[in] len  The length (including the zero-byte at the end) of the\n                    string to be read.\n    @param[in,out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 1\n    @return `true` if the string was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_string(const NumberType len, string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 1))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                    exception_message(input_format_t::bson, concat(\"string length must be at least 1, is \", std::to_string(len)), \"string\"), nullptr));\n        }\n\n        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();\n    }\n\n    /*!\n    @brief Parses a byte array input of length @a len from the BSON input.\n    @param[in] len  The length of the byte array to be read.\n    @param[in,out] result  A reference to the binary variable where the read\n                            array is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 0\n    @return `true` if the byte array was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_binary(const NumberType len, binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(len < 0))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                    exception_message(input_format_t::bson, concat(\"byte array length cannot be negative, is \", std::to_string(len)), \"binary\"), nullptr));\n        }\n\n        // All BSON binary values have a subtype\n        std::uint8_t subtype{};\n        get_number<std::uint8_t>(input_format_t::bson, subtype);\n        result.set_subtype(subtype);\n\n        return get_binary(input_format_t::bson, len, result);\n    }\n\n    /*!\n    @brief Read a BSON document element of the given @a element_type.\n    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html\n    @param[in] element_type_parse_position The position in the input stream,\n               where the `element_type` was read.\n    @warning Not all BSON element types are supported yet. An unsupported\n             @a element_type will give rise to a parse_error.114:\n             Unsupported BSON record type 0x...\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_internal(const char_int_type element_type,\n                                     const std::size_t element_type_parse_position)\n    {\n        switch (element_type)\n        {\n            case 0x01: // double\n            {\n                double number{};\n                return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0x02: // string\n            {\n                std::int32_t len{};\n                string_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);\n            }\n\n            case 0x03: // object\n            {\n                return parse_bson_internal();\n            }\n\n            case 0x04: // array\n            {\n                return parse_bson_array();\n            }\n\n            case 0x05: // binary\n            {\n                std::int32_t len{};\n                binary_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);\n            }\n\n            case 0x08: // boolean\n            {\n                return sax->boolean(get() != 0);\n            }\n\n            case 0x0A: // null\n            {\n                return sax->null();\n            }\n\n            case 0x10: // int32\n            {\n                std::int32_t value{};\n                return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            case 0x12: // int64\n            {\n                std::int64_t value{};\n                return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);\n            }\n\n            default: // anything else not supported (yet)\n            {\n                std::array<char, 3> cr{{}};\n                static_cast<void>((std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                std::string cr_str{cr.data()};\n                return sax->parse_error(element_type_parse_position, cr_str,\n                                        parse_error::create(114, element_type_parse_position, concat(\"Unsupported BSON record type 0x\", cr_str), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @brief Read a BSON element list (as specified in the BSON-spec)\n\n    The same binary layout is used for objects and arrays, hence it must be\n    indicated with the argument @a is_array which one is expected\n    (true --> array, false --> object).\n\n    @param[in] is_array Determines if the element list being read is to be\n                        treated as an object (@a is_array == false), or as an\n                        array (@a is_array == true).\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_list(const bool is_array)\n    {\n        string_t key;\n\n        while (auto element_type = get())\n        {\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, \"element list\")))\n            {\n                return false;\n            }\n\n            const std::size_t element_type_parse_position = chars_read;\n            if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))\n            {\n                return false;\n            }\n\n            if (!is_array && !sax->key(key))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))\n            {\n                return false;\n            }\n\n            // get_bson_cstr only appends\n            key.clear();\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief Reads an array from the BSON input and passes it to the SAX-parser.\n    @return whether a valid BSON-array was passed to the SAX parser\n    */\n    bool parse_bson_array()\n    {\n        std::int32_t document_size{};\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))\n        {\n            return false;\n        }\n\n        return sax->end_array();\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true) or whether the last read character should\n                         be considered instead (false)\n    @param[in] tag_handler how CBOR tags should be treated\n\n    @return whether a valid CBOR value was passed to the SAX parser\n    */\n    bool parse_cbor_internal(const bool get_char,\n                             const cbor_tag_handler_t tag_handler)\n    {\n        switch (get_char ? get() : current)\n        {\n            // EOF\n            case std::char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::cbor, \"value\");\n\n            // Integer 0x00..0x17 (0..23)\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            case 0x18: // Unsigned integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x19: // Unsigned integer (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1A: // Unsigned integer (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);\n            }\n\n            // Negative integer -1-0x00..-1-0x17 (-1..-24)\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n                return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));\n\n            case 0x38: // Negative integer (one-byte uint8_t follows)\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)\n                        - static_cast<number_integer_t>(number));\n            }\n\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            case 0x5F: // Binary data (indefinite length)\n            {\n                binary_t b;\n                return get_cbor_binary(b) && sax->binary(b);\n            }\n\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                string_t s;\n                return get_cbor_string(s) && sax->string(s);\n            }\n\n            // array (0x00..0x17 data items follow)\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n                return get_cbor_array(\n                           conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0x98: // array (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x99: // array (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9A: // array (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9B: // array (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0x9F: // array (indefinite length)\n                return get_cbor_array(static_cast<std::size_t>(-1), tag_handler);\n\n            // map (0x00..0x17 pairs of data items follow)\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n                return get_cbor_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);\n\n            case 0xB8: // map (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xB9: // map (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBA: // map (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBB: // map (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);\n            }\n\n            case 0xBF: // map (indefinite length)\n                return get_cbor_object(static_cast<std::size_t>(-1), tag_handler);\n\n            case 0xC6: // tagged item\n            case 0xC7:\n            case 0xC8:\n            case 0xC9:\n            case 0xCA:\n            case 0xCB:\n            case 0xCC:\n            case 0xCD:\n            case 0xCE:\n            case 0xCF:\n            case 0xD0:\n            case 0xD1:\n            case 0xD2:\n            case 0xD3:\n            case 0xD4:\n            case 0xD8: // tagged item (1 bytes follow)\n            case 0xD9: // tagged item (2 bytes follow)\n            case 0xDA: // tagged item (4 bytes follow)\n            case 0xDB: // tagged item (8 bytes follow)\n            {\n                switch (tag_handler)\n                {\n                    case cbor_tag_handler_t::error:\n                    {\n                        auto last_token = get_token_string();\n                        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                                exception_message(input_format_t::cbor, concat(\"invalid byte: 0x\", last_token), \"value\"), nullptr));\n                    }\n\n                    case cbor_tag_handler_t::ignore:\n                    {\n                        // ignore binary subtype\n                        switch (current)\n                        {\n                            case 0xD8:\n                            {\n                                std::uint8_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xD9:\n                            {\n                                std::uint16_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xDA:\n                            {\n                                std::uint32_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            case 0xDB:\n                            {\n                                std::uint64_t subtype_to_ignore{};\n                                get_number(input_format_t::cbor, subtype_to_ignore);\n                                break;\n                            }\n                            default:\n                                break;\n                        }\n                        return parse_cbor_internal(true, tag_handler);\n                    }\n\n                    case cbor_tag_handler_t::store:\n                    {\n                        binary_t b;\n                        // use binary subtype and store in binary container\n                        switch (current)\n                        {\n                            case 0xD8:\n                            {\n                                std::uint8_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xD9:\n                            {\n                                std::uint16_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xDA:\n                            {\n                                std::uint32_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            case 0xDB:\n                            {\n                                std::uint64_t subtype{};\n                                get_number(input_format_t::cbor, subtype);\n                                b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));\n                                break;\n                            }\n                            default:\n                                return parse_cbor_internal(true, tag_handler);\n                        }\n                        get();\n                        return get_cbor_binary(b) && sax->binary(b);\n                    }\n\n                    default:                 // LCOV_EXCL_LINE\n                        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n                        return false;        // LCOV_EXCL_LINE\n                }\n            }\n\n            case 0xF4: // false\n                return sax->boolean(false);\n\n            case 0xF5: // true\n                return sax->boolean(true);\n\n            case 0xF6: // null\n                return sax->null();\n\n            case 0xF9: // Half-Precision Float (two-byte IEEE 754)\n            {\n                const auto byte1_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n                const auto byte2_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n\n                const auto byte1 = static_cast<unsigned char>(byte1_raw);\n                const auto byte2 = static_cast<unsigned char>(byte2_raw);\n\n                // code from RFC 7049, Appendix D, Figure 3:\n                // As half-precision floating-point numbers were only added\n                // to IEEE 754 in 2008, today's programming platforms often\n                // still only have limited support for them. It is very\n                // easy to include at least decoding support for them even\n                // without such support. An example of a small decoder for\n                // half-precision floating-point numbers in the C language\n                // is shown in Fig. 3.\n                const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);\n                const double val = [&half]\n                {\n                    const int exp = (half >> 10u) & 0x1Fu;\n                    const unsigned int mant = half & 0x3FFu;\n                    JSON_ASSERT(0 <= exp&& exp <= 32);\n                    JSON_ASSERT(mant <= 1024);\n                    switch (exp)\n                    {\n                        case 0:\n                            return std::ldexp(mant, -24);\n                        case 31:\n                            return (mant == 0)\n                            ? std::numeric_limits<double>::infinity()\n                            : std::numeric_limits<double>::quiet_NaN();\n                        default:\n                            return std::ldexp(mant + 1024, exp - 25);\n                    }\n                }();\n                return sax->number_float((half & 0x8000u) != 0\n                                         ? static_cast<number_float_t>(-val)\n                                         : static_cast<number_float_t>(val), \"\");\n            }\n\n            case 0xFA: // Single-Precision Float (four-byte IEEE 754)\n            {\n                float number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)\n            {\n                double number{};\n                return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            default: // anything else (0xFF is handled inside the other types)\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                        exception_message(input_format_t::cbor, concat(\"invalid byte: 0x\", last_token), \"value\"), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n    Additionally, CBOR's strings with indefinite lengths are supported.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_cbor_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            {\n                return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    string_t chunk;\n                    if (!get_cbor_string(chunk))\n                    {\n                        return false;\n                    }\n                    result.append(chunk);\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,\n                                        exception_message(input_format_t::cbor, concat(\"expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x\", last_token), \"string\"), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into the byte array.\n    Additionally, CBOR's byte arrays with indefinite lengths are supported.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_cbor_binary(binary_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, \"binary\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // Binary data (0x00..0x17 bytes follow)\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            {\n                return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0x58: // Binary data (one-byte uint8_t for n follows)\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x59: // Binary data (two-byte uint16_t for n follow)\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5A: // Binary data (four-byte uint32_t for n follow)\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5B: // Binary data (eight-byte uint64_t for n follow)\n            {\n                std::uint64_t len{};\n                return get_number(input_format_t::cbor, len) &&\n                       get_binary(input_format_t::cbor, len, result);\n            }\n\n            case 0x5F: // Binary data (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    binary_t chunk;\n                    if (!get_cbor_binary(chunk))\n                    {\n                        return false;\n                    }\n                    result.insert(result.end(), chunk.begin(), chunk.end());\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,\n                                        exception_message(input_format_t::cbor, concat(\"expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x\", last_token), \"binary\"), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array or static_cast<std::size_t>(-1) for an\n                    array of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether array creation completed\n    */\n    bool get_cbor_array(const std::size_t len,\n                        const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        if (len != static_cast<std::size_t>(-1))\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))\n                {\n                    return false;\n                }\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object or static_cast<std::size_t>(-1) for an\n                    object of indefinite size\n    @param[in] tag_handler how CBOR tags should be treated\n    @return whether object creation completed\n    */\n    bool get_cbor_object(const std::size_t len,\n                         const cbor_tag_handler_t tag_handler)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        if (len != 0)\n        {\n            string_t key;\n            if (len != static_cast<std::size_t>(-1))\n            {\n                for (std::size_t i = 0; i < len; ++i)\n                {\n                    get();\n                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n\n                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                while (get() != 0xFF)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n\n                    if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    /*!\n    @return whether a valid MessagePack value was passed to the SAX parser\n    */\n    bool parse_msgpack_internal()\n    {\n        switch (get())\n        {\n            // EOF\n            case std::char_traits<char_type>::eof():\n                return unexpect_eof(input_format_t::msgpack, \"value\");\n\n            // positive fixint\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n            case 0x18:\n            case 0x19:\n            case 0x1A:\n            case 0x1B:\n            case 0x1C:\n            case 0x1D:\n            case 0x1E:\n            case 0x1F:\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n            case 0x38:\n            case 0x39:\n            case 0x3A:\n            case 0x3B:\n            case 0x3C:\n            case 0x3D:\n            case 0x3E:\n            case 0x3F:\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58:\n            case 0x59:\n            case 0x5A:\n            case 0x5B:\n            case 0x5C:\n            case 0x5D:\n            case 0x5E:\n            case 0x5F:\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78:\n            case 0x79:\n            case 0x7A:\n            case 0x7B:\n            case 0x7C:\n            case 0x7D:\n            case 0x7E:\n            case 0x7F:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            // fixmap\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n                return get_msgpack_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixarray\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n            case 0x98:\n            case 0x99:\n            case 0x9A:\n            case 0x9B:\n            case 0x9C:\n            case 0x9D:\n            case 0x9E:\n            case 0x9F:\n                return get_msgpack_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));\n\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            case 0xD9: // str 8\n            case 0xDA: // str 16\n            case 0xDB: // str 32\n            {\n                string_t s;\n                return get_msgpack_string(s) && sax->string(s);\n            }\n\n            case 0xC0: // nil\n                return sax->null();\n\n            case 0xC2: // false\n                return sax->boolean(false);\n\n            case 0xC3: // true\n                return sax->boolean(true);\n\n            case 0xC4: // bin 8\n            case 0xC5: // bin 16\n            case 0xC6: // bin 32\n            case 0xC7: // ext 8\n            case 0xC8: // ext 16\n            case 0xC9: // ext 32\n            case 0xD4: // fixext 1\n            case 0xD5: // fixext 2\n            case 0xD6: // fixext 4\n            case 0xD7: // fixext 8\n            case 0xD8: // fixext 16\n            {\n                binary_t b;\n                return get_msgpack_binary(b) && sax->binary(b);\n            }\n\n            case 0xCA: // float 32\n            {\n                float number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCB: // float 64\n            {\n                double number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCC: // uint 8\n            {\n                std::uint8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCD: // uint 16\n            {\n                std::uint16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCE: // uint 32\n            {\n                std::uint32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xCF: // uint 64\n            {\n                std::uint64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);\n            }\n\n            case 0xD0: // int 8\n            {\n                std::int8_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD1: // int 16\n            {\n                std::int16_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD2: // int 32\n            {\n                std::int32_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xD3: // int 64\n            {\n                std::int64_t number{};\n                return get_number(input_format_t::msgpack, number) && sax->number_integer(number);\n            }\n\n            case 0xDC: // array 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDD: // array 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_array(conditional_static_cast<std::size_t>(len));\n            }\n\n            case 0xDE: // map 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xDF: // map 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_msgpack_object(conditional_static_cast<std::size_t>(len));\n            }\n\n            // negative fixint\n            case 0xE0:\n            case 0xE1:\n            case 0xE2:\n            case 0xE3:\n            case 0xE4:\n            case 0xE5:\n            case 0xE6:\n            case 0xE7:\n            case 0xE8:\n            case 0xE9:\n            case 0xEA:\n            case 0xEB:\n            case 0xEC:\n            case 0xED:\n            case 0xEE:\n            case 0xEF:\n            case 0xF0:\n            case 0xF1:\n            case 0xF2:\n            case 0xF3:\n            case 0xF4:\n            case 0xF5:\n            case 0xF6:\n            case 0xF7:\n            case 0xF8:\n            case 0xF9:\n            case 0xFA:\n            case 0xFB:\n            case 0xFC:\n            case 0xFD:\n            case 0xFE:\n            case 0xFF:\n                return sax->number_integer(static_cast<std::int8_t>(current));\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                        exception_message(input_format_t::msgpack, concat(\"invalid byte: 0x\", last_token), \"value\"), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_msgpack_string(string_t& result)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            {\n                return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);\n            }\n\n            case 0xD9: // str 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDA: // str 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDB: // str 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,\n                                        exception_message(input_format_t::msgpack, concat(\"expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x\", last_token), \"string\"), nullptr));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack byte array\n\n    This function first reads starting bytes to determine the expected\n    byte array length and then copies this number of bytes into a byte array.\n\n    @param[out] result  created byte array\n\n    @return whether byte array creation completed\n    */\n    bool get_msgpack_binary(binary_t& result)\n    {\n        // helper function to set the subtype\n        auto assign_and_return_true = [&result](std::int8_t subtype)\n        {\n            result.set_subtype(static_cast<std::uint8_t>(subtype));\n            return true;\n        };\n\n        switch (current)\n        {\n            case 0xC4: // bin 8\n            {\n                std::uint8_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC5: // bin 16\n            {\n                std::uint16_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC6: // bin 32\n            {\n                std::uint32_t len{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_binary(input_format_t::msgpack, len, result);\n            }\n\n            case 0xC7: // ext 8\n            {\n                std::uint8_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC8: // ext 16\n            {\n                std::uint16_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xC9: // ext 32\n            {\n                std::uint32_t len{};\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, len) &&\n                       get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, len, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD4: // fixext 1\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 1, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD5: // fixext 2\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 2, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD6: // fixext 4\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 4, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD7: // fixext 8\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 8, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            case 0xD8: // fixext 16\n            {\n                std::int8_t subtype{};\n                return get_number(input_format_t::msgpack, subtype) &&\n                       get_binary(input_format_t::msgpack, 16, result) &&\n                       assign_and_return_true(subtype);\n            }\n\n            default:           // LCOV_EXCL_LINE\n                return false;  // LCOV_EXCL_LINE\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array\n    @return whether array creation completed\n    */\n    bool get_msgpack_array(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))\n        {\n            return false;\n        }\n\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object\n    @return whether object creation completed\n    */\n    bool get_msgpack_object(const std::size_t len)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))\n            {\n                return false;\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))\n            {\n                return false;\n            }\n            key.clear();\n        }\n\n        return sax->end_object();\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether a valid UBJSON value was passed to the SAX parser\n    */\n    bool parse_ubjson_internal(const bool get_char = true)\n    {\n        return get_ubjson_value(get_char ? get_ignore_noop() : current);\n    }\n\n    /*!\n    @brief reads a UBJSON string\n\n    This function is either called after reading the 'S' byte explicitly\n    indicating a string, or in case of an object key where the 'S' byte can be\n    left out.\n\n    @param[out] result   created string\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether string creation completed\n    */\n    bool get_ubjson_string(string_t& result, const bool get_char = true)\n    {\n        if (get_char)\n        {\n            get();  // TODO(niels): may we ignore N here?\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"value\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            case 'U':\n            {\n                std::uint8_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'i':\n            {\n                std::int8_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'I':\n            {\n                std::int16_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'l':\n            {\n                std::int32_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'L':\n            {\n                std::int64_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'u':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint16_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'm':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint32_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            case 'M':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint64_t len{};\n                return get_number(input_format, len) && get_string(input_format, len, result);\n            }\n\n            default:\n                break;\n        }\n        auto last_token = get_token_string();\n        std::string message;\n\n        if (input_format != input_format_t::bjdata)\n        {\n            message = \"expected length type specification (U, i, I, l, L); last byte: 0x\" + last_token;\n        }\n        else\n        {\n            message = \"expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x\" + last_token;\n        }\n        return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, \"string\"), nullptr));\n    }\n\n    /*!\n    @param[out] dim  an integer vector storing the ND array dimensions\n    @return whether reading ND array size vector is successful\n    */\n    bool get_ubjson_ndarray_size(std::vector<size_t>& dim)\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        size_t dimlen = 0;\n        bool no_ndarray = true;\n\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type, no_ndarray)))\n        {\n            return false;\n        }\n\n        if (size_and_type.first != npos)\n        {\n            if (size_and_type.second != 0)\n            {\n                if (size_and_type.second != 'N')\n                {\n                    for (std::size_t i = 0; i < size_and_type.first; ++i)\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second)))\n                        {\n                            return false;\n                        }\n                        dim.push_back(dimlen);\n                    }\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray)))\n                    {\n                        return false;\n                    }\n                    dim.push_back(dimlen);\n                }\n            }\n        }\n        else\n        {\n            while (current != ']')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, current)))\n                {\n                    return false;\n                }\n                dim.push_back(dimlen);\n                get_ignore_noop();\n            }\n        }\n        return true;\n    }\n\n    /*!\n    @param[out] result  determined size\n    @param[in,out] is_ndarray  for input, `true` means already inside an ndarray vector\n                               or ndarray dimension is not allowed; `false` means ndarray\n                               is allowed; for output, `true` means an ndarray is found;\n                               is_ndarray can only return `true` when its initial value\n                               is `false`\n    @param[in] prefix  type marker if already read, otherwise set to 0\n\n    @return whether size determination completed\n    */\n    bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)\n    {\n        if (prefix == 0)\n        {\n            prefix = get_ignore_noop();\n        }\n\n        switch (prefix)\n        {\n            case 'U':\n            {\n                std::uint8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                if (number < 0)\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,\n                                            exception_message(input_format, \"count in an optimized container must be positive\", \"size\"), nullptr));\n                }\n                result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char\n                return true;\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                if (number < 0)\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,\n                                            exception_message(input_format, \"count in an optimized container must be positive\", \"size\"), nullptr));\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                if (number < 0)\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,\n                                            exception_message(input_format, \"count in an optimized container must be positive\", \"size\"), nullptr));\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                if (number < 0)\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,\n                                            exception_message(input_format, \"count in an optimized container must be positive\", \"size\"), nullptr));\n                }\n                if (!value_in_range_of<std::size_t>(number))\n                {\n                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,\n                                            exception_message(input_format, \"integer value overflow\", \"size\"), nullptr));\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'u':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint16_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'm':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint32_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                result = conditional_static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'M':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint64_t number{};\n                if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))\n                {\n                    return false;\n                }\n                if (!value_in_range_of<std::size_t>(number))\n                {\n                    return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,\n                                            exception_message(input_format, \"integer value overflow\", \"size\"), nullptr));\n                }\n                result = detail::conditional_static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case '[':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                if (is_ndarray) // ndarray dimensional vector can only contain integers, and can not embed another array\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, \"ndarray dimentional vector is not allowed\", \"size\"), nullptr));\n                }\n                std::vector<size_t> dim;\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))\n                {\n                    return false;\n                }\n                if (dim.size() == 1 || (dim.size() == 2 && dim.at(0) == 1)) // return normal array size if 1D row vector\n                {\n                    result = dim.at(dim.size() - 1);\n                    return true;\n                }\n                if (!dim.empty())  // if ndarray, convert to an object in JData annotated array format\n                {\n                    for (auto i : dim) // test if any dimension in an ndarray is 0, if so, return a 1D empty container\n                    {\n                        if ( i == 0 )\n                        {\n                            result = 0;\n                            return true;\n                        }\n                    }\n\n                    string_t key = \"_ArraySize_\";\n                    if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))\n                    {\n                        return false;\n                    }\n                    result = 1;\n                    for (auto i : dim)\n                    {\n                        result *= i;\n                        if (result == 0 || result == npos) // because dim elements shall not have zeros, result = 0 means overflow happened; it also can't be npos as it is used to initialize size in get_ubjson_size_type()\n                        {\n                            return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, \"excessive ndarray size caused overflow\", \"size\"), nullptr));\n                        }\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))\n                        {\n                            return false;\n                        }\n                    }\n                    is_ndarray = true;\n                    return sax->end_array();\n                }\n                result = 0;\n                return true;\n            }\n\n            default:\n                break;\n        }\n        auto last_token = get_token_string();\n        std::string message;\n\n        if (input_format != input_format_t::bjdata)\n        {\n            message = \"expected length type specification (U, i, I, l, L) after '#'; last byte: 0x\" + last_token;\n        }\n        else\n        {\n            message = \"expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x\" + last_token;\n        }\n        return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, \"size\"), nullptr));\n    }\n\n    /*!\n    @brief determine the type and size for a container\n\n    In the optimized UBJSON format, a type and a size can be provided to allow\n    for a more compact representation.\n\n    @param[out] result  pair of the size and the type\n    @param[in] inside_ndarray  whether the parser is parsing an ND array dimensional vector\n\n    @return whether pair creation completed\n    */\n    bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result, bool inside_ndarray = false)\n    {\n        result.first = npos; // size\n        result.second = 0; // type\n        bool is_ndarray = false;\n\n        get_ignore_noop();\n\n        if (current == '$')\n        {\n            result.second = get();  // must not ignore 'N', because 'N' maybe the type\n            if (input_format == input_format_t::bjdata\n                    && JSON_HEDLEY_UNLIKELY(std::binary_search(bjd_optimized_type_markers.begin(), bjd_optimized_type_markers.end(), result.second)))\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                        exception_message(input_format, concat(\"marker 0x\", last_token, \" is not a permitted optimized array type\"), \"type\"), nullptr));\n            }\n\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"type\")))\n            {\n                return false;\n            }\n\n            get_ignore_noop();\n            if (JSON_HEDLEY_UNLIKELY(current != '#'))\n            {\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"value\")))\n                {\n                    return false;\n                }\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                        exception_message(input_format, concat(\"expected '#' after type information; last byte: 0x\", last_token), \"size\"), nullptr));\n            }\n\n            bool is_error = get_ubjson_size_value(result.first, is_ndarray);\n            if (input_format == input_format_t::bjdata && is_ndarray)\n            {\n                if (inside_ndarray)\n                {\n                    return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,\n                                            exception_message(input_format, \"ndarray can not be recursive\", \"size\"), nullptr));\n                }\n                result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters\n            }\n            return is_error;\n        }\n\n        if (current == '#')\n        {\n            bool is_error = get_ubjson_size_value(result.first, is_ndarray);\n            if (input_format == input_format_t::bjdata && is_ndarray)\n            {\n                return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,\n                                        exception_message(input_format, \"ndarray requires both type and size\", \"size\"), nullptr));\n            }\n            return is_error;\n        }\n\n        return true;\n    }\n\n    /*!\n    @param prefix  the previously read or set type prefix\n    @return whether value creation completed\n    */\n    bool get_ubjson_value(const char_int_type prefix)\n    {\n        switch (prefix)\n        {\n            case std::char_traits<char_type>::eof():  // EOF\n                return unexpect_eof(input_format, \"value\");\n\n            case 'T':  // true\n                return sax->boolean(true);\n            case 'F':  // false\n                return sax->boolean(false);\n\n            case 'Z':  // null\n                return sax->null();\n\n            case 'U':\n            {\n                std::uint8_t number{};\n                return get_number(input_format, number) && sax->number_unsigned(number);\n            }\n\n            case 'i':\n            {\n                std::int8_t number{};\n                return get_number(input_format, number) && sax->number_integer(number);\n            }\n\n            case 'I':\n            {\n                std::int16_t number{};\n                return get_number(input_format, number) && sax->number_integer(number);\n            }\n\n            case 'l':\n            {\n                std::int32_t number{};\n                return get_number(input_format, number) && sax->number_integer(number);\n            }\n\n            case 'L':\n            {\n                std::int64_t number{};\n                return get_number(input_format, number) && sax->number_integer(number);\n            }\n\n            case 'u':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint16_t number{};\n                return get_number(input_format, number) && sax->number_unsigned(number);\n            }\n\n            case 'm':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint32_t number{};\n                return get_number(input_format, number) && sax->number_unsigned(number);\n            }\n\n            case 'M':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                std::uint64_t number{};\n                return get_number(input_format, number) && sax->number_unsigned(number);\n            }\n\n            case 'h':\n            {\n                if (input_format != input_format_t::bjdata)\n                {\n                    break;\n                }\n                const auto byte1_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"number\")))\n                {\n                    return false;\n                }\n                const auto byte2_raw = get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"number\")))\n                {\n                    return false;\n                }\n\n                const auto byte1 = static_cast<unsigned char>(byte1_raw);\n                const auto byte2 = static_cast<unsigned char>(byte2_raw);\n\n                // code from RFC 7049, Appendix D, Figure 3:\n                // As half-precision floating-point numbers were only added\n                // to IEEE 754 in 2008, today's programming platforms often\n                // still only have limited support for them. It is very\n                // easy to include at least decoding support for them even\n                // without such support. An example of a small decoder for\n                // half-precision floating-point numbers in the C language\n                // is shown in Fig. 3.\n                const auto half = static_cast<unsigned int>((byte2 << 8u) + byte1);\n                const double val = [&half]\n                {\n                    const int exp = (half >> 10u) & 0x1Fu;\n                    const unsigned int mant = half & 0x3FFu;\n                    JSON_ASSERT(0 <= exp&& exp <= 32);\n                    JSON_ASSERT(mant <= 1024);\n                    switch (exp)\n                    {\n                        case 0:\n                            return std::ldexp(mant, -24);\n                        case 31:\n                            return (mant == 0)\n                            ? std::numeric_limits<double>::infinity()\n                            : std::numeric_limits<double>::quiet_NaN();\n                        default:\n                            return std::ldexp(mant + 1024, exp - 25);\n                    }\n                }();\n                return sax->number_float((half & 0x8000u) != 0\n                                         ? static_cast<number_float_t>(-val)\n                                         : static_cast<number_float_t>(val), \"\");\n            }\n\n            case 'd':\n            {\n                float number{};\n                return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'D':\n            {\n                double number{};\n                return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'H':\n            {\n                return get_ubjson_high_precision_number();\n            }\n\n            case 'C':  // char\n            {\n                get();\n                if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"char\")))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(current > 127))\n                {\n                    auto last_token = get_token_string();\n                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,\n                                            exception_message(input_format, concat(\"byte after 'C' must be in range 0x00..0x7F; last byte: 0x\", last_token), \"char\"), nullptr));\n                }\n                string_t s(1, static_cast<typename string_t::value_type>(current));\n                return sax->string(s);\n            }\n\n            case 'S':  // string\n            {\n                string_t s;\n                return get_ubjson_string(s) && sax->string(s);\n            }\n\n            case '[':  // array\n                return get_ubjson_array();\n\n            case '{':  // object\n                return get_ubjson_object();\n\n            default: // anything else\n                break;\n        }\n        auto last_token = get_token_string();\n        return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format, \"invalid byte: 0x\" + last_token, \"value\"), nullptr));\n    }\n\n    /*!\n    @return whether array creation completed\n    */\n    bool get_ubjson_array()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        // if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):\n        // {\"_ArrayType_\" : \"typeid\", \"_ArraySize_\" : [n1, n2, ...], \"_ArrayData_\" : [v1, v2, ...]}\n\n        if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)\n        {\n            size_and_type.second &= ~(static_cast<char_int_type>(1) << 8);  // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker\n            auto it = std::lower_bound(bjd_types_map.begin(), bjd_types_map.end(), size_and_type.second, [](const bjd_type & p, char_int_type t)\n            {\n                return p.first < t;\n            });\n            string_t key = \"_ArrayType_\";\n            if (JSON_HEDLEY_UNLIKELY(it == bjd_types_map.end() || it->first != size_and_type.second))\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                        exception_message(input_format, \"invalid byte: 0x\" + last_token, \"type\"), nullptr));\n            }\n\n            string_t type = it->second; // sax->string() takes a reference\n            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second == 'C')\n            {\n                size_and_type.second = 'U';\n            }\n\n            key = \"_ArrayData_\";\n            if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))\n            {\n                return false;\n            }\n\n            for (std::size_t i = 0; i < size_and_type.first; ++i)\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                {\n                    return false;\n                }\n            }\n\n            return (sax->end_array() && sax->end_object());\n        }\n\n        if (size_and_type.first != npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                if (size_and_type.second != 'N')\n                {\n                    for (std::size_t i = 0; i < size_and_type.first; ++i)\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                        {\n                            return false;\n                        }\n                    }\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))\n            {\n                return false;\n            }\n\n            while (current != ']')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @return whether object creation completed\n    */\n    bool get_ubjson_object()\n    {\n        std::pair<std::size_t, char_int_type> size_and_type;\n        if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        // do not accept ND-array size in objects in BJData\n        if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,\n                                    exception_message(input_format, \"BJData object does not support ND-array size in optimized format\", \"object\"), nullptr));\n        }\n\n        string_t key;\n        if (size_and_type.first != npos)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n        else\n        {\n            if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))\n            {\n                return false;\n            }\n\n            while (current != '}')\n            {\n                if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))\n                {\n                    return false;\n                }\n                if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    // Note, no reader for UBJSON binary types is implemented because they do\n    // not exist\n\n    bool get_ubjson_high_precision_number()\n    {\n        // get size of following number string\n        std::size_t size{};\n        bool no_ndarray = true;\n        auto res = get_ubjson_size_value(size, no_ndarray);\n        if (JSON_HEDLEY_UNLIKELY(!res))\n        {\n            return res;\n        }\n\n        // get number string\n        std::vector<char> number_vector;\n        for (std::size_t i = 0; i < size; ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, \"number\")))\n            {\n                return false;\n            }\n            number_vector.push_back(static_cast<char>(current));\n        }\n\n        // parse number string\n        using ia_type = decltype(detail::input_adapter(number_vector));\n        auto number_lexer = detail::lexer<BasicJsonType, ia_type>(detail::input_adapter(number_vector), false);\n        const auto result_number = number_lexer.scan();\n        const auto number_string = number_lexer.get_token_string();\n        const auto result_remainder = number_lexer.scan();\n\n        using token_type = typename detail::lexer_base<BasicJsonType>::token_type;\n\n        if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))\n        {\n            return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,\n                                    exception_message(input_format, concat(\"invalid number text: \", number_lexer.get_token_string()), \"high-precision number\"), nullptr));\n        }\n\n        switch (result_number)\n        {\n            case token_type::value_integer:\n                return sax->number_integer(number_lexer.get_number_integer());\n            case token_type::value_unsigned:\n                return sax->number_unsigned(number_lexer.get_number_unsigned());\n            case token_type::value_float:\n                return sax->number_float(number_lexer.get_number_float(), std::move(number_string));\n            case token_type::uninitialized:\n            case token_type::literal_true:\n            case token_type::literal_false:\n            case token_type::literal_null:\n            case token_type::value_string:\n            case token_type::begin_array:\n            case token_type::begin_object:\n            case token_type::end_array:\n            case token_type::end_object:\n            case token_type::name_separator:\n            case token_type::value_separator:\n            case token_type::parse_error:\n            case token_type::end_of_input:\n            case token_type::literal_or_value:\n            default:\n                return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,\n                                        exception_message(input_format, concat(\"invalid number text: \", number_lexer.get_token_string()), \"high-precision number\"), nullptr));\n        }\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*!\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a -'ve valued\n    `std::char_traits<char_type>::eof()` in that case.\n\n    @return character read from the input\n    */\n    char_int_type get()\n    {\n        ++chars_read;\n        return current = ia.get_character();\n    }\n\n    /*!\n    @return character read from the input after ignoring all 'N' entries\n    */\n    char_int_type get_ignore_noop()\n    {\n        do\n        {\n            get();\n        }\n        while (current == 'N');\n\n        return current;\n    }\n\n    /*\n    @brief read a number from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format   the current format (for diagnostics)\n    @param[out] result  number of type @a NumberType\n\n    @return whether conversion completed\n\n    @note This function needs to respect the system's endianness, because\n          bytes in CBOR, MessagePack, and UBJSON are stored in network order\n          (big endian) and therefore need reordering on little endian systems.\n          On the other hand, BSON and BJData use little endian and should reorder\n          on big endian systems.\n    */\n    template<typename NumberType, bool InputIsLittleEndian = false>\n    bool get_number(const input_format_t format, NumberType& result)\n    {\n        // step 1: read input into array with system's byte order\n        std::array<std::uint8_t, sizeof(NumberType)> vec{};\n        for (std::size_t i = 0; i < sizeof(NumberType); ++i)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"number\")))\n            {\n                return false;\n            }\n\n            // reverse byte order prior to conversion if necessary\n            if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata))\n            {\n                vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);\n            }\n            else\n            {\n                vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE\n            }\n        }\n\n        // step 2: convert array into number of type T and return\n        std::memcpy(&result, vec.data(), sizeof(NumberType));\n        return true;\n    }\n\n    /*!\n    @brief create a string by reading characters from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of characters to read\n    @param[out] result string created by reading @a len bytes\n\n    @return whether string creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of string memory.\n    */\n    template<typename NumberType>\n    bool get_string(const input_format_t format,\n                    const NumberType len,\n                    string_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"string\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<typename string_t::value_type>(current));\n        }\n        return success;\n    }\n\n    /*!\n    @brief create a byte array by reading bytes from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of bytes to read\n    @param[out] result byte array created by reading @a len bytes\n\n    @return whether byte array creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of memory.\n    */\n    template<typename NumberType>\n    bool get_binary(const input_format_t format,\n                    const NumberType len,\n                    binary_t& result)\n    {\n        bool success = true;\n        for (NumberType i = 0; i < len; i++)\n        {\n            get();\n            if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, \"binary\")))\n            {\n                success = false;\n                break;\n            }\n            result.push_back(static_cast<std::uint8_t>(current));\n        }\n        return success;\n    }\n\n    /*!\n    @param[in] format   the current format (for diagnostics)\n    @param[in] context  further context information (for diagnostics)\n    @return whether the last read character is not EOF\n    */\n    JSON_HEDLEY_NON_NULL(3)\n    bool unexpect_eof(const input_format_t format, const char* context) const\n    {\n        if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))\n        {\n            return sax->parse_error(chars_read, \"<end of file>\",\n                                    parse_error::create(110, chars_read, exception_message(format, \"unexpected end of input\", context), nullptr));\n        }\n        return true;\n    }\n\n    /*!\n    @return a string representation of the last read byte\n    */\n    std::string get_token_string() const\n    {\n        std::array<char, 3> cr{{}};\n        static_cast<void>((std::snprintf)(cr.data(), cr.size(), \"%.2hhX\", static_cast<unsigned char>(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        return std::string{cr.data()};\n    }\n\n    /*!\n    @param[in] format   the current format\n    @param[in] detail   a detailed error message\n    @param[in] context  further context information\n    @return a message string to use in the parse_error exceptions\n    */\n    std::string exception_message(const input_format_t format,\n                                  const std::string& detail,\n                                  const std::string& context) const\n    {\n        std::string error_msg = \"syntax error while parsing \";\n\n        switch (format)\n        {\n            case input_format_t::cbor:\n                error_msg += \"CBOR\";\n                break;\n\n            case input_format_t::msgpack:\n                error_msg += \"MessagePack\";\n                break;\n\n            case input_format_t::ubjson:\n                error_msg += \"UBJSON\";\n                break;\n\n            case input_format_t::bson:\n                error_msg += \"BSON\";\n                break;\n\n            case input_format_t::bjdata:\n                error_msg += \"BJData\";\n                break;\n\n            case input_format_t::json: // LCOV_EXCL_LINE\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n\n        return concat(error_msg, ' ', context, \": \", detail);\n    }\n\n  private:\n    static JSON_INLINE_VARIABLE constexpr std::size_t npos = static_cast<std::size_t>(-1);\n\n    /// input adapter\n    InputAdapterType ia;\n\n    /// the current character\n    char_int_type current = std::char_traits<char_type>::eof();\n\n    /// the number of characters read\n    std::size_t chars_read = 0;\n\n    /// whether we can assume little endianness\n    const bool is_little_endian = little_endianness();\n\n    /// input format\n    const input_format_t input_format = input_format_t::json;\n\n    /// the SAX parser\n    json_sax_t* sax = nullptr;\n\n    // excluded markers in bjdata optimized type\n#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \\\n    make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')\n\n#define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \\\n    make_array<bjd_type>(                      \\\n    bjd_type{'C', \"char\"},                     \\\n    bjd_type{'D', \"double\"},                   \\\n    bjd_type{'I', \"int16\"},                    \\\n    bjd_type{'L', \"int64\"},                    \\\n    bjd_type{'M', \"uint64\"},                   \\\n    bjd_type{'U', \"uint8\"},                    \\\n    bjd_type{'d', \"single\"},                   \\\n    bjd_type{'i', \"int8\"},                     \\\n    bjd_type{'l', \"int32\"},                    \\\n    bjd_type{'m', \"uint32\"},                   \\\n    bjd_type{'u', \"uint16\"})\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    // lookup tables\n    // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)\n    const decltype(JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_) bjd_optimized_type_markers =\n        JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_;\n\n    using bjd_type = std::pair<char_int_type, string_t>;\n    // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)\n    const decltype(JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_) bjd_types_map =\n        JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_;\n\n#undef JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_\n#undef JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_\n};\n\n#ifndef JSON_HAS_CPP_17\n    template<typename BasicJsonType, typename InputAdapterType, typename SAX>\n    constexpr std::size_t binary_reader<BasicJsonType, InputAdapterType, SAX>::npos;\n#endif\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n// #include <nlohmann/detail/input/parser.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <cmath> // isfinite\n#include <cstdint> // uint8_t\n#include <functional> // function\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n\n// #include <nlohmann/detail/string_concat.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n////////////\n// parser //\n////////////\n\nenum class parse_event_t : std::uint8_t\n{\n    /// the parser read `{` and started to process a JSON object\n    object_start,\n    /// the parser read `}` and finished processing a JSON object\n    object_end,\n    /// the parser read `[` and started to process a JSON array\n    array_start,\n    /// the parser read `]` and finished processing a JSON array\n    array_end,\n    /// the parser read a key of a value in an object\n    key,\n    /// the parser finished reading a JSON value\n    value\n};\n\ntemplate<typename BasicJsonType>\nusing parser_callback_t =\n    std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;\n\n/*!\n@brief syntax analysis\n\nThis class implements a recursive descent parser.\n*/\ntemplate<typename BasicJsonType, typename InputAdapterType>\nclass parser\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using lexer_t = lexer<BasicJsonType, InputAdapterType>;\n    using token_type = typename lexer_t::token_type;\n\n  public:\n    /// a parser reading from an input adapter\n    explicit parser(InputAdapterType&& adapter,\n                    const parser_callback_t<BasicJsonType> cb = nullptr,\n                    const bool allow_exceptions_ = true,\n                    const bool skip_comments = false)\n        : callback(cb)\n        , m_lexer(std::move(adapter), skip_comments)\n        , allow_exceptions(allow_exceptions_)\n    {\n        // read first token\n        get_token();\n    }\n\n    /*!\n    @brief public parser interface\n\n    @param[in] strict      whether to expect the last token to be EOF\n    @param[in,out] result  parsed JSON value\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n    */\n    void parse(const bool strict, BasicJsonType& result)\n    {\n        if (callback)\n        {\n            json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);\n            sax_parse_internal(&sdp);\n\n            // in strict mode, input must be completely read\n            if (strict && (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(),\n                                                    exception_message(token_type::end_of_input, \"value\"), nullptr));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n\n            // set top-level value to null if it was discarded by the callback\n            // function\n            if (result.is_discarded())\n            {\n                result = nullptr;\n            }\n        }\n        else\n        {\n            json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);\n            sax_parse_internal(&sdp);\n\n            // in strict mode, input must be completely read\n            if (strict && (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, \"value\"), nullptr));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n        }\n\n        result.assert_invariant();\n    }\n\n    /*!\n    @brief public accept interface\n\n    @param[in] strict  whether to expect the last token to be EOF\n    @return whether the input is a proper JSON text\n    */\n    bool accept(const bool strict = true)\n    {\n        json_sax_acceptor<BasicJsonType> sax_acceptor;\n        return sax_parse(&sax_acceptor, strict);\n    }\n\n    template<typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse(SAX* sax, const bool strict = true)\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n        const bool result = sax_parse_internal(sax);\n\n        // strict mode: next byte must be EOF\n        if (result && strict && (get_token() != token_type::end_of_input))\n        {\n            return sax->parse_error(m_lexer.get_position(),\n                                    m_lexer.get_token_string(),\n                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, \"value\"), nullptr));\n        }\n\n        return result;\n    }\n\n  private:\n    template<typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    bool sax_parse_internal(SAX* sax)\n    {\n        // stack to remember the hierarchy of structured values we are parsing\n        // true = array; false = object\n        std::vector<bool> states;\n        // value to avoid a goto (see comment where set to true)\n        bool skip_to_state_evaluation = false;\n\n        while (true)\n        {\n            if (!skip_to_state_evaluation)\n            {\n                // invariant: get_token() was called before each iteration\n                switch (last_token)\n                {\n                    case token_type::begin_object:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing } -> we are done\n                        if (get_token() == token_type::end_object)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // parse key\n                        if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, \"object key\"), nullptr));\n                        }\n                        if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        // parse separator (:)\n                        if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, \"object separator\"), nullptr));\n                        }\n\n                        // remember we are now inside an object\n                        states.push_back(false);\n\n                        // parse values\n                        get_token();\n                        continue;\n                    }\n\n                    case token_type::begin_array:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing ] -> we are done\n                        if (get_token() == token_type::end_array)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // remember we are now inside an array\n                        states.push_back(true);\n\n                        // parse values (no need to call get_token)\n                        continue;\n                    }\n\n                    case token_type::value_float:\n                    {\n                        const auto res = m_lexer.get_number_float();\n\n                        if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    out_of_range::create(406, concat(\"number overflow parsing '\", m_lexer.get_token_string(), '\\''), nullptr));\n                        }\n\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        break;\n                    }\n\n                    case token_type::literal_false:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_null:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->null()))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_true:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_integer:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_string:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_unsigned:\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::parse_error:\n                    {\n                        // using \"uninitialized\" to avoid \"expected\" message\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, \"value\"), nullptr));\n                    }\n\n                    case token_type::uninitialized:\n                    case token_type::end_array:\n                    case token_type::end_object:\n                    case token_type::name_separator:\n                    case token_type::value_separator:\n                    case token_type::end_of_input:\n                    case token_type::literal_or_value:\n                    default: // the last token was unexpected\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, \"value\"), nullptr));\n                    }\n                }\n            }\n            else\n            {\n                skip_to_state_evaluation = false;\n            }\n\n            // we reached this line after we successfully parsed a value\n            if (states.empty())\n            {\n                // empty stack: we reached the end of the hierarchy: done\n                return true;\n            }\n\n            if (states.back())  // array\n            {\n                // comma -> next value\n                if (get_token() == token_type::value_separator)\n                {\n                    // parse a new value\n                    get_token();\n                    continue;\n                }\n\n                // closing ]\n                if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))\n                {\n                    if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))\n                    {\n                        return false;\n                    }\n\n                    // We are done with this array. Before we can parse a\n                    // new value, we need to evaluate the new state first.\n                    // By setting skip_to_state_evaluation to false, we\n                    // are effectively jumping to the beginning of this if.\n                    JSON_ASSERT(!states.empty());\n                    states.pop_back();\n                    skip_to_state_evaluation = true;\n                    continue;\n                }\n\n                return sax->parse_error(m_lexer.get_position(),\n                                        m_lexer.get_token_string(),\n                                        parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, \"array\"), nullptr));\n            }\n\n            // states.back() is false -> object\n\n            // comma -> next value\n            if (get_token() == token_type::value_separator)\n            {\n                // parse key\n                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))\n                {\n                    return sax->parse_error(m_lexer.get_position(),\n                                            m_lexer.get_token_string(),\n                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, \"object key\"), nullptr));\n                }\n\n                if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))\n                {\n                    return false;\n                }\n\n                // parse separator (:)\n                if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))\n                {\n                    return sax->parse_error(m_lexer.get_position(),\n                                            m_lexer.get_token_string(),\n                                            parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, \"object separator\"), nullptr));\n                }\n\n                // parse values\n                get_token();\n                continue;\n            }\n\n            // closing }\n            if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))\n            {\n                if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))\n                {\n                    return false;\n                }\n\n                // We are done with this object. Before we can parse a\n                // new value, we need to evaluate the new state first.\n                // By setting skip_to_state_evaluation to false, we\n                // are effectively jumping to the beginning of this if.\n                JSON_ASSERT(!states.empty());\n                states.pop_back();\n                skip_to_state_evaluation = true;\n                continue;\n            }\n\n            return sax->parse_error(m_lexer.get_position(),\n                                    m_lexer.get_token_string(),\n                                    parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, \"object\"), nullptr));\n        }\n    }\n\n    /// get next token from lexer\n    token_type get_token()\n    {\n        return last_token = m_lexer.scan();\n    }\n\n    std::string exception_message(const token_type expected, const std::string& context)\n    {\n        std::string error_msg = \"syntax error \";\n\n        if (!context.empty())\n        {\n            error_msg += concat(\"while parsing \", context, ' ');\n        }\n\n        error_msg += \"- \";\n\n        if (last_token == token_type::parse_error)\n        {\n            error_msg += concat(m_lexer.get_error_message(), \"; last read: '\",\n                                m_lexer.get_token_string(), '\\'');\n        }\n        else\n        {\n            error_msg += concat(\"unexpected \", lexer_t::token_type_name(last_token));\n        }\n\n        if (expected != token_type::uninitialized)\n        {\n            error_msg += concat(\"; expected \", lexer_t::token_type_name(expected));\n        }\n\n        return error_msg;\n    }\n\n  private:\n    /// callback function\n    const parser_callback_t<BasicJsonType> callback = nullptr;\n    /// the type of the last read token\n    token_type last_token = token_type::uninitialized;\n    /// the lexer\n    lexer_t m_lexer;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <cstddef> // ptrdiff_t\n#include <limits>  // numeric_limits\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/*\n@brief an iterator for primitive JSON types\n\nThis class models an iterator for primitive JSON types (boolean, number,\nstring). It's only purpose is to allow the iterator/const_iterator classes\nto \"iterate\" over primitive values. Internally, the iterator is modeled by\na `difference_type` variable. Value begin_value (`0`) models the begin,\nend_value (`1`) models past the end.\n*/\nclass primitive_iterator_t\n{\n  private:\n    using difference_type = std::ptrdiff_t;\n    static constexpr difference_type begin_value = 0;\n    static constexpr difference_type end_value = begin_value + 1;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /// iterator as signed integer type\n    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();\n\n  public:\n    constexpr difference_type get_value() const noexcept\n    {\n        return m_it;\n    }\n\n    /// set iterator to a defined beginning\n    void set_begin() noexcept\n    {\n        m_it = begin_value;\n    }\n\n    /// set iterator to a defined past the end\n    void set_end() noexcept\n    {\n        m_it = end_value;\n    }\n\n    /// return whether the iterator can be dereferenced\n    constexpr bool is_begin() const noexcept\n    {\n        return m_it == begin_value;\n    }\n\n    /// return whether the iterator is at end\n    constexpr bool is_end() const noexcept\n    {\n        return m_it == end_value;\n    }\n\n    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it == rhs.m_it;\n    }\n\n    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it < rhs.m_it;\n    }\n\n    primitive_iterator_t operator+(difference_type n) noexcept\n    {\n        auto result = *this;\n        result += n;\n        return result;\n    }\n\n    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it - rhs.m_it;\n    }\n\n    primitive_iterator_t& operator++() noexcept\n    {\n        ++m_it;\n        return *this;\n    }\n\n    primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp)\n    {\n        auto result = *this;\n        ++m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator--() noexcept\n    {\n        --m_it;\n        return *this;\n    }\n\n    primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp)\n    {\n        auto result = *this;\n        --m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator+=(difference_type n) noexcept\n    {\n        m_it += n;\n        return *this;\n    }\n\n    primitive_iterator_t& operator-=(difference_type n) noexcept\n    {\n        m_it -= n;\n        return *this;\n    }\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/*!\n@brief an iterator value\n\n@note This structure could easily be a union, but MSVC currently does not allow\nunions members with complex constructors, see https://github.com/nlohmann/json/pull/105.\n*/\ntemplate<typename BasicJsonType> struct internal_iterator\n{\n    /// iterator for JSON objects\n    typename BasicJsonType::object_t::iterator object_iterator {};\n    /// iterator for JSON arrays\n    typename BasicJsonType::array_t::iterator array_iterator {};\n    /// generic iterator for all other types\n    primitive_iterator_t primitive_iterator {};\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/iterators/iter_impl.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next\n#include <type_traits> // conditional, is_const, remove_const\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n// forward declare, to be able to friend it later on\ntemplate<typename IteratorType> class iteration_proxy;\ntemplate<typename IteratorType> class iteration_proxy_value;\n\n/*!\n@brief a template for a bidirectional iterator for the @ref basic_json class\nThis class implements a both iterators (iterator and const_iterator) for the\n@ref basic_json class.\n@note An iterator is called *initialized* when a pointer to a JSON value has\n      been set (e.g., by a constructor or a copy assignment). If the iterator is\n      default-constructed, it is *uninitialized* and most methods are undefined.\n      **The library uses assertions to detect calls on uninitialized iterators.**\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n@since version 1.0.0, simplified in version 2.0.9, change to bidirectional\n       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)\n*/\ntemplate<typename BasicJsonType>\nclass iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)\n{\n    /// the iterator with BasicJsonType of different const-ness\n    using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;\n    /// allow basic_json to access private members\n    friend other_iter_impl;\n    friend BasicJsonType;\n    friend iteration_proxy<iter_impl>;\n    friend iteration_proxy_value<iter_impl>;\n\n    using object_t = typename BasicJsonType::object_t;\n    using array_t = typename BasicJsonType::array_t;\n    // make sure BasicJsonType is basic_json or const basic_json\n    static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,\n                  \"iter_impl only accepts (const) basic_json\");\n    // superficial check for the LegacyBidirectionalIterator named requirement\n    static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value\n                  &&  std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<typename array_t::iterator>::iterator_category>::value,\n                  \"basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.\");\n\n  public:\n    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.\n    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.\n    /// A user-defined iterator should provide publicly accessible typedefs named\n    /// iterator_category, value_type, difference_type, pointer, and reference.\n    /// Note that value_type is required to be non-const, even for constant iterators.\n    using iterator_category = std::bidirectional_iterator_tag;\n\n    /// the type of the values when the iterator is dereferenced\n    using value_type = typename BasicJsonType::value_type;\n    /// a type to represent differences between iterators\n    using difference_type = typename BasicJsonType::difference_type;\n    /// defines a pointer to the type iterated over (value_type)\n    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,\n          typename BasicJsonType::const_pointer,\n          typename BasicJsonType::pointer>::type;\n    /// defines a reference to the type iterated over (value_type)\n    using reference =\n        typename std::conditional<std::is_const<BasicJsonType>::value,\n        typename BasicJsonType::const_reference,\n        typename BasicJsonType::reference>::type;\n\n    iter_impl() = default;\n    ~iter_impl() = default;\n    iter_impl(iter_impl&&) noexcept = default;\n    iter_impl& operator=(iter_impl&&) noexcept = default;\n\n    /*!\n    @brief constructor for a given JSON instance\n    @param[in] object  pointer to a JSON object for this iterator\n    @pre object != nullptr\n    @post The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    explicit iter_impl(pointer object) noexcept : m_object(object)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = typename object_t::iterator();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = typename array_t::iterator();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator = primitive_iterator_t();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @note The conventional copy constructor and copy assignment are implicitly\n          defined. Combined with the following converting constructor and\n          assignment, they support: (1) copy from iterator to iterator, (2)\n          copy from const iterator to const iterator, and (3) conversion from\n          iterator to const iterator. However conversion from const iterator\n          to iterator is not defined.\n    */\n\n    /*!\n    @brief const copy constructor\n    @param[in] other const iterator to copy from\n    @note This copy constructor had to be defined explicitly to circumvent a bug\n          occurring on msvc v19.0 compiler (VS 2015) debug build. For more\n          information refer to: https://github.com/nlohmann/json/issues/1608\n    */\n    iter_impl(const iter_impl<const BasicJsonType>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept\n    {\n        if (&other != this)\n        {\n            m_object = other.m_object;\n            m_it = other.m_it;\n        }\n        return *this;\n    }\n\n    /*!\n    @brief converting constructor\n    @param[in] other  non-const iterator to copy from\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it)\n    {}\n\n    /*!\n    @brief converting assignment\n    @param[in] other  non-const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)\n    {\n        m_object = other.m_object;\n        m_it = other.m_it;\n        return *this;\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief set the iterator to the first value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_begin() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->begin();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->begin();\n                break;\n            }\n\n            case value_t::null:\n            {\n                // set to end so begin()==end() is true: null is empty\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator.set_begin();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @brief set the iterator past the last value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_end() noexcept\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->end();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->end();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n        }\n    }\n\n  public:\n    /*!\n    @brief return a reference to the value pointed to by the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator*() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());\n                return m_it.object_iterator->second;\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());\n                return *m_it.array_iterator;\n            }\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", m_object));\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief dereference the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    pointer operator->() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());\n                return &(m_it.object_iterator->second);\n            }\n\n            case value_t::array:\n            {\n                JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());\n                return &*m_it.array_iterator;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief post-increment (it++)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp)\n    {\n        auto result = *this;\n        ++(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-increment (++it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator++()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, 1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, 1);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                ++m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief post-decrement (it--)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp)\n    {\n        auto result = *this;\n        --(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-decrement (--it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator--()\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, -1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, -1);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                --m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief comparison: equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >\n    bool operator==(const IterImpl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\", m_object));\n        }\n\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                return (m_it.object_iterator == other.m_it.object_iterator);\n\n            case value_t::array:\n                return (m_it.array_iterator == other.m_it.array_iterator);\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return (m_it.primitive_iterator == other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief comparison: not equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >\n    bool operator!=(const IterImpl& other) const\n    {\n        return !operator==(other);\n    }\n\n    /*!\n    @brief comparison: smaller\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\", m_object));\n        }\n\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(213, \"cannot compare order of object iterators\", m_object));\n\n            case value_t::array:\n                return (m_it.array_iterator < other.m_it.array_iterator);\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return (m_it.primitive_iterator < other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<=(const iter_impl& other) const\n    {\n        return !other.operator < (*this);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>(const iter_impl& other) const\n    {\n        return !operator<=(other);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>=(const iter_impl& other) const\n    {\n        return !operator<(other);\n    }\n\n    /*!\n    @brief add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator+=(difference_type i)\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\", m_object));\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, i);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                m_it.primitive_iterator += i;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator-=(difference_type i)\n    {\n        return operator+=(-i);\n    }\n\n    /*!\n    @brief add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator+(difference_type i) const\n    {\n        auto result = *this;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief addition of distance and iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    friend iter_impl operator+(difference_type i, const iter_impl& it)\n    {\n        auto result = it;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator-(difference_type i) const\n    {\n        auto result = *this;\n        result -= i;\n        return result;\n    }\n\n    /*!\n    @brief return difference\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    difference_type operator-(const iter_impl& other) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\", m_object));\n\n            case value_t::array:\n                return m_it.array_iterator - other.m_it.array_iterator;\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                return m_it.primitive_iterator - other.m_it.primitive_iterator;\n        }\n    }\n\n    /*!\n    @brief access to successor\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator[](difference_type n) const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(208, \"cannot use operator[] for object iterators\", m_object));\n\n            case value_t::array:\n                return *std::next(m_it.array_iterator, n);\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", m_object));\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\", m_object));\n            }\n        }\n    }\n\n    /*!\n    @brief return the key of an object iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    const typename object_t::key_type& key() const\n    {\n        JSON_ASSERT(m_object != nullptr);\n\n        if (JSON_HEDLEY_LIKELY(m_object->is_object()))\n        {\n            return m_it.object_iterator->first;\n        }\n\n        JSON_THROW(invalid_iterator::create(207, \"cannot use key() for non-object iterators\", m_object));\n    }\n\n    /*!\n    @brief return the value of an iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference value() const\n    {\n        return operator*();\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /// associated JSON instance\n    pointer m_object = nullptr;\n    /// the actual iterator of the associated instance\n    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n\n// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <cstddef> // ptrdiff_t\n#include <iterator> // reverse_iterator\n#include <utility> // declval\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n//////////////////////\n// reverse_iterator //\n//////////////////////\n\n/*!\n@brief a template for a reverse iterator class\n\n@tparam Base the base iterator type to reverse. Valid types are @ref\niterator (to create @ref reverse_iterator) and @ref const_iterator (to\ncreate @ref const_reverse_iterator).\n\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):\n  It is possible to write to the pointed-to element (only if @a Base is\n  @ref iterator).\n\n@since version 1.0.0\n*/\ntemplate<typename Base>\nclass json_reverse_iterator : public std::reverse_iterator<Base>\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    /// shortcut to the reverse iterator adapter\n    using base_iterator = std::reverse_iterator<Base>;\n    /// the reference type for the pointed-to element\n    using reference = typename Base::reference;\n\n    /// create reverse iterator from iterator\n    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept\n        : base_iterator(it) {}\n\n    /// create reverse iterator from base class\n    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}\n\n    /// post-increment (it++)\n    json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));\n    }\n\n    /// pre-increment (++it)\n    json_reverse_iterator& operator++()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator++());\n    }\n\n    /// post-decrement (it--)\n    json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));\n    }\n\n    /// pre-decrement (--it)\n    json_reverse_iterator& operator--()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator--());\n    }\n\n    /// add to iterator\n    json_reverse_iterator& operator+=(difference_type i)\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));\n    }\n\n    /// add to iterator\n    json_reverse_iterator operator+(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));\n    }\n\n    /// subtract from iterator\n    json_reverse_iterator operator-(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));\n    }\n\n    /// return difference\n    difference_type operator-(const json_reverse_iterator& other) const\n    {\n        return base_iterator(*this) - base_iterator(other);\n    }\n\n    /// access to successor\n    reference operator[](difference_type n) const\n    {\n        return *(this->operator+(n));\n    }\n\n    /// return the key of an object iterator\n    auto key() const -> decltype(std::declval<Base>().key())\n    {\n        auto it = --this->base();\n        return it.key();\n    }\n\n    /// return the value of an iterator\n    reference value() const\n    {\n        auto it = --this->base();\n        return it.operator * ();\n    }\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n// #include <nlohmann/detail/json_pointer.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <algorithm> // all_of\n#include <cctype> // isdigit\n#include <cerrno> // errno, ERANGE\n#include <cstdlib> // strtoull\n#ifndef JSON_NO_IO\n    #include <iosfwd> // ostream\n#endif  // JSON_NO_IO\n#include <limits> // max\n#include <numeric> // accumulate\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/string_concat.hpp>\n\n// #include <nlohmann/detail/string_escape.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document\n/// @sa https://json.nlohmann.me/api/json_pointer/\ntemplate<typename RefStringType>\nclass json_pointer\n{\n    // allow basic_json to access private members\n    NLOHMANN_BASIC_JSON_TPL_DECLARATION\n    friend class basic_json;\n\n    template<typename>\n    friend class json_pointer;\n\n    template<typename T>\n    struct string_t_helper\n    {\n        using type = T;\n    };\n\n    NLOHMANN_BASIC_JSON_TPL_DECLARATION\n    struct string_t_helper<NLOHMANN_BASIC_JSON_TPL>\n    {\n        using type = StringType;\n    };\n\n  public:\n    // for backwards compatibility accept BasicJsonType\n    using string_t = typename string_t_helper<RefStringType>::type;\n\n    /// @brief create JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/\n    explicit json_pointer(const string_t& s = \"\")\n        : reference_tokens(split(s))\n    {}\n\n    /// @brief return a string representation of the JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/to_string/\n    string_t to_string() const\n    {\n        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),\n                               string_t{},\n                               [](const string_t& a, const string_t& b)\n        {\n            return detail::concat(a, '/', detail::escape(b));\n        });\n    }\n\n    /// @brief return a string representation of the JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())\n    operator string_t() const\n    {\n        return to_string();\n    }\n\n#ifndef JSON_NO_IO\n    /// @brief write string representation of the JSON pointer to stream\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/\n    friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr)\n    {\n        o << ptr.to_string();\n        return o;\n    }\n#endif\n\n    /// @brief append another JSON pointer at the end of this JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/\n    json_pointer& operator/=(const json_pointer& ptr)\n    {\n        reference_tokens.insert(reference_tokens.end(),\n                                ptr.reference_tokens.begin(),\n                                ptr.reference_tokens.end());\n        return *this;\n    }\n\n    /// @brief append an unescaped reference token at the end of this JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/\n    json_pointer& operator/=(string_t token)\n    {\n        push_back(std::move(token));\n        return *this;\n    }\n\n    /// @brief append an array index at the end of this JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/\n    json_pointer& operator/=(std::size_t array_idx)\n    {\n        return *this /= std::to_string(array_idx);\n    }\n\n    /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/\n    friend json_pointer operator/(const json_pointer& lhs,\n                                  const json_pointer& rhs)\n    {\n        return json_pointer(lhs) /= rhs;\n    }\n\n    /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/\n    friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)\n    {\n        return json_pointer(lhs) /= std::move(token);\n    }\n\n    /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/\n    friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx)\n    {\n        return json_pointer(lhs) /= array_idx;\n    }\n\n    /// @brief returns the parent of this JSON pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/\n    json_pointer parent_pointer() const\n    {\n        if (empty())\n        {\n            return *this;\n        }\n\n        json_pointer res = *this;\n        res.pop_back();\n        return res;\n    }\n\n    /// @brief remove last reference token\n    /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/\n    void pop_back()\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", nullptr));\n        }\n\n        reference_tokens.pop_back();\n    }\n\n    /// @brief return last reference token\n    /// @sa https://json.nlohmann.me/api/json_pointer/back/\n    const string_t& back() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", nullptr));\n        }\n\n        return reference_tokens.back();\n    }\n\n    /// @brief append an unescaped token at the end of the reference pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/push_back/\n    void push_back(const string_t& token)\n    {\n        reference_tokens.push_back(token);\n    }\n\n    /// @brief append an unescaped token at the end of the reference pointer\n    /// @sa https://json.nlohmann.me/api/json_pointer/push_back/\n    void push_back(string_t&& token)\n    {\n        reference_tokens.push_back(std::move(token));\n    }\n\n    /// @brief return whether pointer points to the root document\n    /// @sa https://json.nlohmann.me/api/json_pointer/empty/\n    bool empty() const noexcept\n    {\n        return reference_tokens.empty();\n    }\n\n  private:\n    /*!\n    @param[in] s  reference token to be converted into an array index\n\n    @return integer representation of @a s\n\n    @throw parse_error.106  if an array index begins with '0'\n    @throw parse_error.109  if an array index begins not with a digit\n    @throw out_of_range.404 if string @a s could not be converted to an integer\n    @throw out_of_range.410 if an array index exceeds size_type\n    */\n    template<typename BasicJsonType>\n    static typename BasicJsonType::size_type array_index(const string_t& s)\n    {\n        using size_type = typename BasicJsonType::size_type;\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))\n        {\n            JSON_THROW(detail::parse_error::create(106, 0, detail::concat(\"array index '\", s, \"' must not begin with '0'\"), nullptr));\n        }\n\n        // error condition (cf. RFC 6901, Sect. 4)\n        if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))\n        {\n            JSON_THROW(detail::parse_error::create(109, 0, detail::concat(\"array index '\", s, \"' is not a number\"), nullptr));\n        }\n\n        const char* p = s.c_str();\n        char* p_end = nullptr;\n        errno = 0; // strtoull doesn't reset errno\n        unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)\n        if (p == p_end // invalid input or empty string\n                || errno == ERANGE // out of range\n                || JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read\n        {\n            JSON_THROW(detail::out_of_range::create(404, detail::concat(\"unresolved reference token '\", s, \"'\"), nullptr));\n        }\n\n        // only triggered on special platforms (like 32bit), see also\n        // https://github.com/nlohmann/json/pull/2203\n        if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)()))  // NOLINT(runtime/int)\n        {\n            JSON_THROW(detail::out_of_range::create(410, detail::concat(\"array index \", s, \" exceeds size_type\"), nullptr));   // LCOV_EXCL_LINE\n        }\n\n        return static_cast<size_type>(res);\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    json_pointer top() const\n    {\n        if (JSON_HEDLEY_UNLIKELY(empty()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\", nullptr));\n        }\n\n        json_pointer result = *this;\n        result.reference_tokens = {reference_tokens[0]};\n        return result;\n    }\n\n  private:\n    /*!\n    @brief create and return a reference to the pointed to value\n\n    @complexity Linear in the number of reference tokens.\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.313 if value cannot be unflattened\n    */\n    template<typename BasicJsonType>\n    BasicJsonType& get_and_create(BasicJsonType& j) const\n    {\n        auto* result = &j;\n\n        // in case no reference tokens exist, return a reference to the JSON value\n        // j which will be overwritten by a primitive value\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (result->type())\n            {\n                case detail::value_t::null:\n                {\n                    if (reference_token == \"0\")\n                    {\n                        // start a new array if reference token is 0\n                        result = &result->operator[](0);\n                    }\n                    else\n                    {\n                        // start a new object otherwise\n                        result = &result->operator[](reference_token);\n                    }\n                    break;\n                }\n\n                case detail::value_t::object:\n                {\n                    // create an entry in the object\n                    result = &result->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    // create an entry in the array\n                    result = &result->operator[](array_index<BasicJsonType>(reference_token));\n                    break;\n                }\n\n                /*\n                The following code is only reached if there exists a reference\n                token _and_ the current value is primitive. In this case, we have\n                an error situation, because primitive values may only occur as\n                single value; that is, with an empty list of reference tokens.\n                */\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::type_error::create(313, \"invalid value to unflatten\", &j));\n            }\n        }\n\n        return *result;\n    }\n\n    /*!\n    @brief return a reference to the pointed to value\n\n    @note This version does not throw if a value is not present, but tries to\n          create nested values instead. For instance, calling this function\n          with pointer `\"/this/that\"` on a null value is equivalent to calling\n          `operator[](\"this\").operator[](\"that\")` on that value, effectively\n          changing the null value to an object.\n\n    @param[in] ptr  a JSON value\n\n    @return reference to the JSON value pointed to by the JSON pointer\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    template<typename BasicJsonType>\n    BasicJsonType& get_unchecked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            // convert null values to arrays or objects before continuing\n            if (ptr->is_null())\n            {\n                // check if reference token is a number\n                const bool nums =\n                    std::all_of(reference_token.begin(), reference_token.end(),\n                                [](const unsigned char x)\n                {\n                    return std::isdigit(x);\n                });\n\n                // change value to array for numbers or \"-\" or to object otherwise\n                *ptr = (nums || reference_token == \"-\")\n                       ? detail::value_t::array\n                       : detail::value_t::object;\n            }\n\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (reference_token == \"-\")\n                    {\n                        // explicitly treat \"-\" as index beyond the end\n                        ptr = &ptr->operator[](ptr->m_value.array->size());\n                    }\n                    else\n                    {\n                        // convert array index to number; unchecked access\n                        ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));\n                    }\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, detail::concat(\"unresolved reference token '\", reference_token, \"'\"), ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    template<typename BasicJsonType>\n    BasicJsonType& get_checked(BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402, detail::concat(\n                                \"array index '-' (\", std::to_string(ptr->m_value.array->size()),\n                                \") is out of range\"), ptr));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index<BasicJsonType>(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, detail::concat(\"unresolved reference token '\", reference_token, \"'\"), ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @brief return a const reference to the pointed to value\n\n    @param[in] ptr  a JSON value\n\n    @return const reference to the JSON value pointed to by the JSON\n    pointer\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    template<typename BasicJsonType>\n    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" cannot be used for const access\n                        JSON_THROW(detail::out_of_range::create(402, detail::concat(\"array index '-' (\", std::to_string(ptr->m_value.array->size()), \") is out of range\"), ptr));\n                    }\n\n                    // use unchecked array access\n                    ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, detail::concat(\"unresolved reference token '\", reference_token, \"'\"), ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    template<typename BasicJsonType>\n    const BasicJsonType& get_checked(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402, detail::concat(\n                                \"array index '-' (\", std::to_string(ptr->m_value.array->size()),\n                                \") is out of range\"), ptr));\n                    }\n\n                    // note: at performs range check\n                    ptr = &ptr->at(array_index<BasicJsonType>(reference_token));\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, detail::concat(\"unresolved reference token '\", reference_token, \"'\"), ptr));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    */\n    template<typename BasicJsonType>\n    bool contains(const BasicJsonType* ptr) const\n    {\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->type())\n            {\n                case detail::value_t::object:\n                {\n                    if (!ptr->contains(reference_token))\n                    {\n                        // we did not find the key in the object\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_HEDLEY_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !(\"0\" <= reference_token && reference_token <= \"9\")))\n                    {\n                        // invalid char\n                        return false;\n                    }\n                    if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))\n                    {\n                        if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))\n                        {\n                            // first char should be between '1' and '9'\n                            return false;\n                        }\n                        for (std::size_t i = 1; i < reference_token.size(); i++)\n                        {\n                            if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))\n                            {\n                                // other char should be between '0' and '9'\n                                return false;\n                            }\n                        }\n                    }\n\n                    const auto idx = array_index<BasicJsonType>(reference_token);\n                    if (idx >= ptr->size())\n                    {\n                        // index out of range\n                        return false;\n                    }\n\n                    ptr = &ptr->operator[](idx);\n                    break;\n                }\n\n                case detail::value_t::null:\n                case detail::value_t::string:\n                case detail::value_t::boolean:\n                case detail::value_t::number_integer:\n                case detail::value_t::number_unsigned:\n                case detail::value_t::number_float:\n                case detail::value_t::binary:\n                case detail::value_t::discarded:\n                default:\n                {\n                    // we do not expect primitive values if there is still a\n                    // reference token to process\n                    return false;\n                }\n            }\n        }\n\n        // no reference token left means we found a primitive value\n        return true;\n    }\n\n    /*!\n    @brief split the string input to reference tokens\n\n    @note This function is only called by the json_pointer constructor.\n          All exceptions below are documented there.\n\n    @throw parse_error.107  if the pointer is not empty or begins with '/'\n    @throw parse_error.108  if character '~' is not followed by '0' or '1'\n    */\n    static std::vector<string_t> split(const string_t& reference_string)\n    {\n        std::vector<string_t> result;\n\n        // special case: empty reference string -> no reference tokens\n        if (reference_string.empty())\n        {\n            return result;\n        }\n\n        // check if nonempty reference string begins with slash\n        if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))\n        {\n            JSON_THROW(detail::parse_error::create(107, 1, detail::concat(\"JSON pointer must be empty or begin with '/' - was: '\", reference_string, \"'\"), nullptr));\n        }\n\n        // extract the reference tokens:\n        // - slash: position of the last read slash (or end of string)\n        // - start: position after the previous slash\n        for (\n            // search for the first slash after the first character\n            std::size_t slash = reference_string.find_first_of('/', 1),\n            // set the beginning of the first reference token\n            start = 1;\n            // we can stop if start == 0 (if slash == string_t::npos)\n            start != 0;\n            // set the beginning of the next reference token\n            // (will eventually be 0 if slash == string_t::npos)\n            start = (slash == string_t::npos) ? 0 : slash + 1,\n            // find next slash\n            slash = reference_string.find_first_of('/', start))\n        {\n            // use the text between the beginning of the reference token\n            // (start) and the last slash (slash).\n            auto reference_token = reference_string.substr(start, slash - start);\n\n            // check reference tokens are properly escaped\n            for (std::size_t pos = reference_token.find_first_of('~');\n                    pos != string_t::npos;\n                    pos = reference_token.find_first_of('~', pos + 1))\n            {\n                JSON_ASSERT(reference_token[pos] == '~');\n\n                // ~ must be followed by 0 or 1\n                if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||\n                                         (reference_token[pos + 1] != '0' &&\n                                          reference_token[pos + 1] != '1')))\n                {\n                    JSON_THROW(detail::parse_error::create(108, 0, \"escape character '~' must be followed with '0' or '1'\", nullptr));\n                }\n            }\n\n            // finally, store the reference token\n            detail::unescape(reference_token);\n            result.push_back(reference_token);\n        }\n\n        return result;\n    }\n\n  private:\n    /*!\n    @param[in] reference_string  the reference string to the current value\n    @param[in] value             the value to consider\n    @param[in,out] result        the result object to insert values to\n\n    @note Empty objects or arrays are flattened to `null`.\n    */\n    template<typename BasicJsonType>\n    static void flatten(const string_t& reference_string,\n                        const BasicJsonType& value,\n                        BasicJsonType& result)\n    {\n        switch (value.type())\n        {\n            case detail::value_t::array:\n            {\n                if (value.m_value.array->empty())\n                {\n                    // flatten empty array as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate array and use index as reference string\n                    for (std::size_t i = 0; i < value.m_value.array->size(); ++i)\n                    {\n                        flatten(detail::concat(reference_string, '/', std::to_string(i)),\n                                value.m_value.array->operator[](i), result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::object:\n            {\n                if (value.m_value.object->empty())\n                {\n                    // flatten empty object as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate object and use keys as reference string\n                    for (const auto& element : *value.m_value.object)\n                    {\n                        flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::null:\n            case detail::value_t::string:\n            case detail::value_t::boolean:\n            case detail::value_t::number_integer:\n            case detail::value_t::number_unsigned:\n            case detail::value_t::number_float:\n            case detail::value_t::binary:\n            case detail::value_t::discarded:\n            default:\n            {\n                // add primitive value with its reference string\n                result[reference_string] = value;\n                break;\n            }\n        }\n    }\n\n    /*!\n    @param[in] value  flattened JSON\n\n    @return unflattened JSON\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n    @throw type_error.313  if value cannot be unflattened\n    */\n    template<typename BasicJsonType>\n    static BasicJsonType\n    unflatten(const BasicJsonType& value)\n    {\n        if (JSON_HEDLEY_UNLIKELY(!value.is_object()))\n        {\n            JSON_THROW(detail::type_error::create(314, \"only objects can be unflattened\", &value));\n        }\n\n        BasicJsonType result;\n\n        // iterate the JSON object values\n        for (const auto& element : *value.m_value.object)\n        {\n            if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))\n            {\n                JSON_THROW(detail::type_error::create(315, \"values in object must be primitive\", &element.second));\n            }\n\n            // assign value to reference pointed to by JSON pointer; Note that if\n            // the JSON pointer is \"\" (i.e., points to the whole value), function\n            // get_and_create returns a reference to result itself. An assignment\n            // will then create a primitive value.\n            json_pointer(element.first).get_and_create(result) = element.second;\n        }\n\n        return result;\n    }\n\n    // can't use conversion operator because of ambiguity\n    json_pointer<string_t> convert() const&\n    {\n        json_pointer<string_t> result;\n        result.reference_tokens = reference_tokens;\n        return result;\n    }\n\n    json_pointer<string_t> convert()&&\n    {\n        json_pointer<string_t> result;\n        result.reference_tokens = std::move(reference_tokens);\n        return result;\n    }\n\n  public:\n#if JSON_HAS_THREE_WAY_COMPARISON\n    /// @brief compares two JSON pointers for equality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/\n    template<typename RefStringTypeRhs>\n    bool operator==(const json_pointer<RefStringTypeRhs>& rhs) const noexcept\n    {\n        return reference_tokens == rhs.reference_tokens;\n    }\n\n    /// @brief compares JSON pointer and string for equality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer))\n    bool operator==(const string_t& rhs) const\n    {\n        return *this == json_pointer(rhs);\n    }\n\n    /// @brief 3-way compares two JSON pointers\n    template<typename RefStringTypeRhs>\n    std::strong_ordering operator<=>(const json_pointer<RefStringTypeRhs>& rhs) const noexcept // *NOPAD*\n    {\n        return  reference_tokens <=> rhs.reference_tokens; // *NOPAD*\n    }\n#else\n    /// @brief compares two JSON pointers for equality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/\n    template<typename RefStringTypeLhs, typename RefStringTypeRhs>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,\n                           const json_pointer<RefStringTypeRhs>& rhs) noexcept;\n\n    /// @brief compares JSON pointer and string for equality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/\n    template<typename RefStringTypeLhs, typename StringType>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,\n                           const StringType& rhs);\n\n    /// @brief compares string and JSON pointer for equality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/\n    template<typename RefStringTypeRhs, typename StringType>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator==(const StringType& lhs,\n                           const json_pointer<RefStringTypeRhs>& rhs);\n\n    /// @brief compares two JSON pointers for inequality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/\n    template<typename RefStringTypeLhs, typename RefStringTypeRhs>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,\n                           const json_pointer<RefStringTypeRhs>& rhs) noexcept;\n\n    /// @brief compares JSON pointer and string for inequality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/\n    template<typename RefStringTypeLhs, typename StringType>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,\n                           const StringType& rhs);\n\n    /// @brief compares string and JSON pointer for inequality\n    /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/\n    template<typename RefStringTypeRhs, typename StringType>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator!=(const StringType& lhs,\n                           const json_pointer<RefStringTypeRhs>& rhs);\n\n    /// @brief compares two JSON pointer for less-than\n    template<typename RefStringTypeLhs, typename RefStringTypeRhs>\n    // NOLINTNEXTLINE(readability-redundant-declaration)\n    friend bool operator<(const json_pointer<RefStringTypeLhs>& lhs,\n                          const json_pointer<RefStringTypeRhs>& rhs) noexcept;\n#endif\n\n  private:\n    /// the reference tokens\n    std::vector<string_t> reference_tokens;\n};\n\n#if !JSON_HAS_THREE_WAY_COMPARISON\n// functions cannot be defined inside class due to ODR violations\ntemplate<typename RefStringTypeLhs, typename RefStringTypeRhs>\ninline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,\n                       const json_pointer<RefStringTypeRhs>& rhs) noexcept\n{\n    return lhs.reference_tokens == rhs.reference_tokens;\n}\n\ntemplate<typename RefStringTypeLhs,\n         typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>\nJSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))\ninline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,\n                       const StringType& rhs)\n{\n    return lhs == json_pointer<RefStringTypeLhs>(rhs);\n}\n\ntemplate<typename RefStringTypeRhs,\n         typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>\nJSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))\ninline bool operator==(const StringType& lhs,\n                       const json_pointer<RefStringTypeRhs>& rhs)\n{\n    return json_pointer<RefStringTypeRhs>(lhs) == rhs;\n}\n\ntemplate<typename RefStringTypeLhs, typename RefStringTypeRhs>\ninline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,\n                       const json_pointer<RefStringTypeRhs>& rhs) noexcept\n{\n    return !(lhs == rhs);\n}\n\ntemplate<typename RefStringTypeLhs,\n         typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>\nJSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))\ninline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,\n                       const StringType& rhs)\n{\n    return !(lhs == rhs);\n}\n\ntemplate<typename RefStringTypeRhs,\n         typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>\nJSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))\ninline bool operator!=(const StringType& lhs,\n                       const json_pointer<RefStringTypeRhs>& rhs)\n{\n    return !(lhs == rhs);\n}\n\ntemplate<typename RefStringTypeLhs, typename RefStringTypeRhs>\ninline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,\n                      const json_pointer<RefStringTypeRhs>& rhs) noexcept\n{\n    return lhs.reference_tokens < rhs.reference_tokens;\n}\n#endif\n\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/json_ref.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <initializer_list>\n#include <utility>\n\n// #include <nlohmann/detail/abi_macros.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\ntemplate<typename BasicJsonType>\nclass json_ref\n{\n  public:\n    using value_type = BasicJsonType;\n\n    json_ref(value_type&& value)\n        : owned_value(std::move(value))\n    {}\n\n    json_ref(const value_type& value)\n        : value_ref(&value)\n    {}\n\n    json_ref(std::initializer_list<json_ref> init)\n        : owned_value(init)\n    {}\n\n    template <\n        class... Args,\n        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >\n    json_ref(Args && ... args)\n        : owned_value(std::forward<Args>(args)...)\n    {}\n\n    // class should be movable only\n    json_ref(json_ref&&) noexcept = default;\n    json_ref(const json_ref&) = delete;\n    json_ref& operator=(const json_ref&) = delete;\n    json_ref& operator=(json_ref&&) = delete;\n    ~json_ref() = default;\n\n    value_type moved_or_copied() const\n    {\n        if (value_ref == nullptr)\n        {\n            return std::move(owned_value);\n        }\n        return *value_ref;\n    }\n\n    value_type const& operator*() const\n    {\n        return value_ref ? *value_ref : owned_value;\n    }\n\n    value_type const* operator->() const\n    {\n        return &** this;\n    }\n\n  private:\n    mutable value_type owned_value = nullptr;\n    value_type const* value_ref = nullptr;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/string_concat.hpp>\n\n// #include <nlohmann/detail/string_escape.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <algorithm> // reverse\n#include <array> // array\n#include <map> // map\n#include <cmath> // isnan, isinf\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstring> // memcpy\n#include <limits> // numeric_limits\n#include <string> // string\n#include <utility> // move\n#include <vector> // vector\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <algorithm> // copy\n#include <cstddef> // size_t\n#include <iterator> // back_inserter\n#include <memory> // shared_ptr, make_shared\n#include <string> // basic_string\n#include <vector> // vector\n\n#ifndef JSON_NO_IO\n    #include <ios>      // streamsize\n    #include <ostream>  // basic_ostream\n#endif  // JSON_NO_IO\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/// abstract output adapter interface\ntemplate<typename CharType> struct output_adapter_protocol\n{\n    virtual void write_character(CharType c) = 0;\n    virtual void write_characters(const CharType* s, std::size_t length) = 0;\n    virtual ~output_adapter_protocol() = default;\n\n    output_adapter_protocol() = default;\n    output_adapter_protocol(const output_adapter_protocol&) = default;\n    output_adapter_protocol(output_adapter_protocol&&) noexcept = default;\n    output_adapter_protocol& operator=(const output_adapter_protocol&) = default;\n    output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default;\n};\n\n/// a type to simplify interfaces\ntemplate<typename CharType>\nusing output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;\n\n/// output adapter for byte vectors\ntemplate<typename CharType, typename AllocatorType = std::allocator<CharType>>\nclass output_vector_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept\n        : v(vec)\n    {}\n\n    void write_character(CharType c) override\n    {\n        v.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        v.insert(v.end(), s, s + length);\n    }\n\n  private:\n    std::vector<CharType, AllocatorType>& v;\n};\n\n#ifndef JSON_NO_IO\n/// output adapter for output streams\ntemplate<typename CharType>\nclass output_stream_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept\n        : stream(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        stream.put(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        stream.write(s, static_cast<std::streamsize>(length));\n    }\n\n  private:\n    std::basic_ostream<CharType>& stream;\n};\n#endif  // JSON_NO_IO\n\n/// output adapter for basic_string\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_string_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_string_adapter(StringType& s) noexcept\n        : str(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        str.push_back(c);\n    }\n\n    JSON_HEDLEY_NON_NULL(2)\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        str.append(s, length);\n    }\n\n  private:\n    StringType& str;\n};\n\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_adapter\n{\n  public:\n    template<typename AllocatorType = std::allocator<CharType>>\n    output_adapter(std::vector<CharType, AllocatorType>& vec)\n        : oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}\n\n#ifndef JSON_NO_IO\n    output_adapter(std::basic_ostream<CharType>& s)\n        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}\n#endif  // JSON_NO_IO\n\n    output_adapter(StringType& s)\n        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}\n\n    operator output_adapter_t<CharType>()\n    {\n        return oa;\n    }\n\n  private:\n    output_adapter_t<CharType> oa = nullptr;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/string_concat.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n///////////////////\n// binary writer //\n///////////////////\n\n/*!\n@brief serialization to CBOR and MessagePack values\n*/\ntemplate<typename BasicJsonType, typename CharType>\nclass binary_writer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using binary_t = typename BasicJsonType::binary_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n\n  public:\n    /*!\n    @brief create a binary writer\n\n    @param[in] adapter  output adapter to write to\n    */\n    explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))\n    {\n        JSON_ASSERT(oa);\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    void write_bson(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n            {\n                write_bson_object(*j.m_value.object);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::array:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                JSON_THROW(type_error::create(317, concat(\"to serialize to BSON, top-level type must be object, but is \", j.type_name()), &j));\n            }\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_cbor(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                oa->write_character(to_char_type(0xF6));\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xF5)\n                                    : to_char_type(0xF4));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // CBOR does not differentiate between positive signed\n                    // integers and unsigned integers. Therefore, we used the\n                    // code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_integer <= 0x17)\n                    {\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x18));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x19));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x1A));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x1B));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    // The conversions below encode the sign in the first\n                    // byte, and the value is converted to a positive number.\n                    const auto positive_number = -1 - j.m_value.number_integer;\n                    if (j.m_value.number_integer >= -24)\n                    {\n                        write_number(static_cast<std::uint8_t>(0x20 + positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x38));\n                        write_number(static_cast<std::uint8_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x39));\n                        write_number(static_cast<std::uint16_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x3A));\n                        write_number(static_cast<std::uint32_t>(positive_number));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x3B));\n                        write_number(static_cast<std::uint64_t>(positive_number));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x18));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x19));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x1A));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_unsigned));\n                }\n                else\n                {\n                    oa->write_character(to_char_type(0x1B));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_unsigned));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                if (std::isnan(j.m_value.number_float))\n                {\n                    // NaN is 0xf97e00 in CBOR\n                    oa->write_character(to_char_type(0xF9));\n                    oa->write_character(to_char_type(0x7E));\n                    oa->write_character(to_char_type(0x00));\n                }\n                else if (std::isinf(j.m_value.number_float))\n                {\n                    // Infinity is 0xf97c00, -Infinity is 0xf9fc00\n                    oa->write_character(to_char_type(0xf9));\n                    oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));\n                    oa->write_character(to_char_type(0x00));\n                }\n                else\n                {\n                    write_compact_float(j.m_value.number_float, detail::input_format_t::cbor);\n                }\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x60 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x78));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x79));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x80 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x98));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x99));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_cbor(el);\n                }\n                break;\n            }\n\n            case value_t::binary:\n            {\n                if (j.m_value.binary->has_subtype())\n                {\n                    if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xd8));\n                        write_number(static_cast<std::uint8_t>(j.m_value.binary->subtype()));\n                    }\n                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xd9));\n                        write_number(static_cast<std::uint16_t>(j.m_value.binary->subtype()));\n                    }\n                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xda));\n                        write_number(static_cast<std::uint32_t>(j.m_value.binary->subtype()));\n                    }\n                    else if (j.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)())\n                    {\n                        write_number(static_cast<std::uint8_t>(0xdb));\n                        write_number(static_cast<std::uint64_t>(j.m_value.binary->subtype()));\n                    }\n                }\n\n                // step 1: write control byte and the binary array size\n                const auto N = j.m_value.binary->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0x40 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x58));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x59));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x5A));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x5B));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                    N);\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<std::uint8_t>(0xA0 + N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB8));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB9));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBA));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBB));\n                    write_number(static_cast<std::uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_cbor(el.first);\n                    write_cbor(el.second);\n                }\n                break;\n            }\n\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_msgpack(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null: // nil\n            {\n                oa->write_character(to_char_type(0xC0));\n                break;\n            }\n\n            case value_t::boolean: // true and false\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xC3)\n                                    : to_char_type(0xC2));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // MessagePack does not differentiate between positive\n                    // signed integers and unsigned integers. Therefore, we used\n                    // the code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_unsigned < 128)\n                    {\n                        // positive fixnum\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                    {\n                        // uint 8\n                        oa->write_character(to_char_type(0xCC));\n                        write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                    {\n                        // uint 16\n                        oa->write_character(to_char_type(0xCD));\n                        write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                    {\n                        // uint 32\n                        oa->write_character(to_char_type(0xCE));\n                        write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                    {\n                        // uint 64\n                        oa->write_character(to_char_type(0xCF));\n                        write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    if (j.m_value.number_integer >= -32)\n                    {\n                        // negative fixnum\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                    {\n                        // int 8\n                        oa->write_character(to_char_type(0xD0));\n                        write_number(static_cast<std::int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                    {\n                        // int 16\n                        oa->write_character(to_char_type(0xD1));\n                        write_number(static_cast<std::int16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                    {\n                        // int 32\n                        oa->write_character(to_char_type(0xD2));\n                        write_number(static_cast<std::int32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&\n                             j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())\n                    {\n                        // int 64\n                        oa->write_character(to_char_type(0xD3));\n                        write_number(static_cast<std::int64_t>(j.m_value.number_integer));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned < 128)\n                {\n                    // positive fixnum\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // uint 8\n                    oa->write_character(to_char_type(0xCC));\n                    write_number(static_cast<std::uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // uint 16\n                    oa->write_character(to_char_type(0xCD));\n                    write_number(static_cast<std::uint16_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // uint 32\n                    oa->write_character(to_char_type(0xCE));\n                    write_number(static_cast<std::uint32_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    // uint 64\n                    oa->write_character(to_char_type(0xCF));\n                    write_number(static_cast<std::uint64_t>(j.m_value.number_integer));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack);\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 31)\n                {\n                    // fixstr\n                    write_number(static_cast<std::uint8_t>(0xA0 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    // str 8\n                    oa->write_character(to_char_type(0xD9));\n                    write_number(static_cast<std::uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // str 16\n                    oa->write_character(to_char_type(0xDA));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // str 32\n                    oa->write_character(to_char_type(0xDB));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 15)\n                {\n                    // fixarray\n                    write_number(static_cast<std::uint8_t>(0x90 | N));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // array 16\n                    oa->write_character(to_char_type(0xDC));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // array 32\n                    oa->write_character(to_char_type(0xDD));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_msgpack(el);\n                }\n                break;\n            }\n\n            case value_t::binary:\n            {\n                // step 0: determine if the binary type has a set subtype to\n                // determine whether or not to use the ext or fixext types\n                const bool use_ext = j.m_value.binary->has_subtype();\n\n                // step 1: write control byte and the byte string length\n                const auto N = j.m_value.binary->size();\n                if (N <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    std::uint8_t output_type{};\n                    bool fixed = true;\n                    if (use_ext)\n                    {\n                        switch (N)\n                        {\n                            case 1:\n                                output_type = 0xD4; // fixext 1\n                                break;\n                            case 2:\n                                output_type = 0xD5; // fixext 2\n                                break;\n                            case 4:\n                                output_type = 0xD6; // fixext 4\n                                break;\n                            case 8:\n                                output_type = 0xD7; // fixext 8\n                                break;\n                            case 16:\n                                output_type = 0xD8; // fixext 16\n                                break;\n                            default:\n                                output_type = 0xC7; // ext 8\n                                fixed = false;\n                                break;\n                        }\n\n                    }\n                    else\n                    {\n                        output_type = 0xC4; // bin 8\n                        fixed = false;\n                    }\n\n                    oa->write_character(to_char_type(output_type));\n                    if (!fixed)\n                    {\n                        write_number(static_cast<std::uint8_t>(N));\n                    }\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    std::uint8_t output_type = use_ext\n                                               ? 0xC8 // ext 16\n                                               : 0xC5; // bin 16\n\n                    oa->write_character(to_char_type(output_type));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    std::uint8_t output_type = use_ext\n                                               ? 0xC9 // ext 32\n                                               : 0xC6; // bin 32\n\n                    oa->write_character(to_char_type(output_type));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 1.5: if this is an ext type, write the subtype\n                if (use_ext)\n                {\n                    write_number(static_cast<std::int8_t>(j.m_value.binary->subtype()));\n                }\n\n                // step 2: write the byte string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                    N);\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 15)\n                {\n                    // fixmap\n                    write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));\n                }\n                else if (N <= (std::numeric_limits<std::uint16_t>::max)())\n                {\n                    // map 16\n                    oa->write_character(to_char_type(0xDE));\n                    write_number(static_cast<std::uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<std::uint32_t>::max)())\n                {\n                    // map 32\n                    oa->write_character(to_char_type(0xDF));\n                    write_number(static_cast<std::uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_msgpack(el.first);\n                    write_msgpack(el.second);\n                }\n                break;\n            }\n\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @param[in] use_count   whether to use '#' prefixes (optimized format)\n    @param[in] use_type    whether to use '$' prefixes (optimized format)\n    @param[in] add_prefix  whether prefixes need to be used for this value\n    @param[in] use_bjdata  whether write in BJData format, default is false\n    */\n    void write_ubjson(const BasicJsonType& j, const bool use_count,\n                      const bool use_type, const bool add_prefix = true,\n                      const bool use_bjdata = false)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('Z'));\n                }\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(j.m_value.boolean\n                                        ? to_char_type('T')\n                                        : to_char_type('F'));\n                }\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix, use_bjdata);\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix, use_bjdata);\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix, use_bjdata);\n                break;\n            }\n\n            case value_t::string:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('S'));\n                }\n                write_number_with_ubjson_prefix(j.m_value.string->size(), true, use_bjdata);\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                bool prefix_required = true;\n                if (use_type && !j.m_value.array->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);\n                    const bool same_prefix = std::all_of(j.begin() + 1, j.end(),\n                                                         [this, first_prefix, use_bjdata](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v, use_bjdata) == first_prefix;\n                    });\n\n                    std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type\n\n                    if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.array->size(), true, use_bjdata);\n                }\n\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_ubjson(el, use_count, use_type, prefix_required, use_bjdata);\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::binary:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                if (use_type && !j.m_value.binary->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    oa->write_character(to_char_type('$'));\n                    oa->write_character('U');\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.binary->size(), true, use_bjdata);\n                }\n\n                if (use_type)\n                {\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(j.m_value.binary->data()),\n                        j.m_value.binary->size());\n                }\n                else\n                {\n                    for (size_t i = 0; i < j.m_value.binary->size(); ++i)\n                    {\n                        oa->write_character(to_char_type('U'));\n                        oa->write_character(j.m_value.binary->data()[i]);\n                    }\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                if (use_bjdata && j.m_value.object->size() == 3 && j.m_value.object->find(\"_ArrayType_\") != j.m_value.object->end() && j.m_value.object->find(\"_ArraySize_\") != j.m_value.object->end() && j.m_value.object->find(\"_ArrayData_\") != j.m_value.object->end())\n                {\n                    if (!write_bjdata_ndarray(*j.m_value.object, use_count, use_type))  // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata)\n                    {\n                        break;\n                    }\n                }\n\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('{'));\n                }\n\n                bool prefix_required = true;\n                if (use_type && !j.m_value.object->empty())\n                {\n                    JSON_ASSERT(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);\n                    const bool same_prefix = std::all_of(j.begin(), j.end(),\n                                                         [this, first_prefix, use_bjdata](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v, use_bjdata) == first_prefix;\n                    });\n\n                    std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type\n\n                    if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.object->size(), true, use_bjdata);\n                }\n\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(el.first.c_str()),\n                        el.first.size());\n                    write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata);\n                }\n\n                if (!use_count)\n                {\n                    oa->write_character(to_char_type('}'));\n                }\n\n                break;\n            }\n\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @return The size of a BSON document entry header, including the id marker\n            and the entry name size (and its null-terminator).\n    */\n    static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)\n    {\n        const auto it = name.find(static_cast<typename string_t::value_type>(0));\n        if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))\n        {\n            JSON_THROW(out_of_range::create(409, concat(\"BSON key cannot contain code point U+0000 (at byte \", std::to_string(it), \")\"), &j));\n            static_cast<void>(j);\n        }\n\n        return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;\n    }\n\n    /*!\n    @brief Writes the given @a element_type and @a name to the output adapter\n    */\n    void write_bson_entry_header(const string_t& name,\n                                 const std::uint8_t element_type)\n    {\n        oa->write_character(to_char_type(element_type)); // boolean\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(name.c_str()),\n            name.size() + 1u);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and boolean value @a value\n    */\n    void write_bson_boolean(const string_t& name,\n                            const bool value)\n    {\n        write_bson_entry_header(name, 0x08);\n        oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and double value @a value\n    */\n    void write_bson_double(const string_t& name,\n                           const double value)\n    {\n        write_bson_entry_header(name, 0x01);\n        write_number<double>(value, true);\n    }\n\n    /*!\n    @return The size of the BSON-encoded string in @a value\n    */\n    static std::size_t calc_bson_string_size(const string_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and string value @a value\n    */\n    void write_bson_string(const string_t& name,\n                           const string_t& value)\n    {\n        write_bson_entry_header(name, 0x02);\n\n        write_number<std::int32_t>(static_cast<std::int32_t>(value.size() + 1ul), true);\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(value.c_str()),\n            value.size() + 1);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and null value\n    */\n    void write_bson_null(const string_t& name)\n    {\n        write_bson_entry_header(name, 0x0A);\n    }\n\n    /*!\n    @return The size of the BSON-encoded integer @a value\n    */\n    static std::size_t calc_bson_integer_size(const std::int64_t value)\n    {\n        return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and integer @a value\n    */\n    void write_bson_integer(const string_t& name,\n                            const std::int64_t value)\n    {\n        if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            write_bson_entry_header(name, 0x10); // int32\n            write_number<std::int32_t>(static_cast<std::int32_t>(value), true);\n        }\n        else\n        {\n            write_bson_entry_header(name, 0x12); // int64\n            write_number<std::int64_t>(static_cast<std::int64_t>(value), true);\n        }\n    }\n\n    /*!\n    @return The size of the BSON-encoded unsigned integer in @a j\n    */\n    static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept\n    {\n        return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and unsigned @a value\n    */\n    void write_bson_unsigned(const string_t& name,\n                             const BasicJsonType& j)\n    {\n        if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x10 /* int32 */);\n            write_number<std::int32_t>(static_cast<std::int32_t>(j.m_value.number_unsigned), true);\n        }\n        else if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x12 /* int64 */);\n            write_number<std::int64_t>(static_cast<std::int64_t>(j.m_value.number_unsigned), true);\n        }\n        else\n        {\n            JSON_THROW(out_of_range::create(407, concat(\"integer number \", std::to_string(j.m_value.number_unsigned), \" cannot be represented by BSON as it does not fit int64\"), &j));\n        }\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and object @a value\n    */\n    void write_bson_object_entry(const string_t& name,\n                                 const typename BasicJsonType::object_t& value)\n    {\n        write_bson_entry_header(name, 0x03); // object\n        write_bson_object(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded array @a value\n    */\n    static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)\n    {\n        std::size_t array_index = 0ul;\n\n        const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast<std::size_t>(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)\n        {\n            return result + calc_bson_element_size(std::to_string(array_index++), el);\n        });\n\n        return sizeof(std::int32_t) + embedded_document_size + 1ul;\n    }\n\n    /*!\n    @return The size of the BSON-encoded binary array @a value\n    */\n    static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and array @a value\n    */\n    void write_bson_array(const string_t& name,\n                          const typename BasicJsonType::array_t& value)\n    {\n        write_bson_entry_header(name, 0x04); // array\n        write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_array_size(value)), true);\n\n        std::size_t array_index = 0ul;\n\n        for (const auto& el : value)\n        {\n            write_bson_element(std::to_string(array_index++), el);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and binary value @a value\n    */\n    void write_bson_binary(const string_t& name,\n                           const binary_t& value)\n    {\n        write_bson_entry_header(name, 0x05);\n\n        write_number<std::int32_t>(static_cast<std::int32_t>(value.size()), true);\n        write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00));\n\n        oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());\n    }\n\n    /*!\n    @brief Calculates the size necessary to serialize the JSON value @a j with its @a name\n    @return The calculated size for the BSON document entry for @a j with the given @a name.\n    */\n    static std::size_t calc_bson_element_size(const string_t& name,\n            const BasicJsonType& j)\n    {\n        const auto header_size = calc_bson_entry_header_size(name, j);\n        switch (j.type())\n        {\n            case value_t::object:\n                return header_size + calc_bson_object_size(*j.m_value.object);\n\n            case value_t::array:\n                return header_size + calc_bson_array_size(*j.m_value.array);\n\n            case value_t::binary:\n                return header_size + calc_bson_binary_size(*j.m_value.binary);\n\n            case value_t::boolean:\n                return header_size + 1ul;\n\n            case value_t::number_float:\n                return header_size + 8ul;\n\n            case value_t::number_integer:\n                return header_size + calc_bson_integer_size(j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);\n\n            case value_t::string:\n                return header_size + calc_bson_string_size(*j.m_value.string);\n\n            case value_t::null:\n                return header_size + 0ul;\n\n            // LCOV_EXCL_START\n            case value_t::discarded:\n            default:\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)\n                return 0ul;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Serializes the JSON value @a j to BSON and associates it with the\n           key @a name.\n    @param name The name to associate with the JSON entity @a j within the\n                current BSON document\n    */\n    void write_bson_element(const string_t& name,\n                            const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n                return write_bson_object_entry(name, *j.m_value.object);\n\n            case value_t::array:\n                return write_bson_array(name, *j.m_value.array);\n\n            case value_t::binary:\n                return write_bson_binary(name, *j.m_value.binary);\n\n            case value_t::boolean:\n                return write_bson_boolean(name, j.m_value.boolean);\n\n            case value_t::number_float:\n                return write_bson_double(name, j.m_value.number_float);\n\n            case value_t::number_integer:\n                return write_bson_integer(name, j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return write_bson_unsigned(name, j);\n\n            case value_t::string:\n                return write_bson_string(name, *j.m_value.string);\n\n            case value_t::null:\n                return write_bson_null(name);\n\n            // LCOV_EXCL_START\n            case value_t::discarded:\n            default:\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)\n                return;\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    /*!\n    @brief Calculates the size of the BSON serialization of the given\n           JSON-object @a j.\n    @param[in] value  JSON value to serialize\n    @pre       value.type() == value_t::object\n    */\n    static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)\n    {\n        std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast<std::size_t>(0),\n                                    [](size_t result, const typename BasicJsonType::object_t::value_type & el)\n        {\n            return result += calc_bson_element_size(el.first, el.second);\n        });\n\n        return sizeof(std::int32_t) + document_size + 1ul;\n    }\n\n    /*!\n    @param[in] value  JSON value to serialize\n    @pre       value.type() == value_t::object\n    */\n    void write_bson_object(const typename BasicJsonType::object_t& value)\n    {\n        write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_object_size(value)), true);\n\n        for (const auto& el : value)\n        {\n            write_bson_element(el.first, el.second);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    static constexpr CharType get_cbor_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xFA);  // Single-Precision Float\n    }\n\n    static constexpr CharType get_cbor_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xFB);  // Double-Precision Float\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    static constexpr CharType get_msgpack_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xCA);  // float 32\n    }\n\n    static constexpr CharType get_msgpack_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xCB);  // float 64\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    // UBJSON: write number (floating point)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_floating_point<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix,\n                                         const bool use_bjdata)\n    {\n        if (add_prefix)\n        {\n            oa->write_character(get_ubjson_float_prefix(n));\n        }\n        write_number(n, use_bjdata);\n    }\n\n    // UBJSON: write number (unsigned integer)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_unsigned<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix,\n                                         const bool use_bjdata)\n    {\n        if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::uint8_t>(n), use_bjdata);\n        }\n        else if (n <= (std::numeric_limits<std::uint8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n), use_bjdata);\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n), use_bjdata);\n        }\n        else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint16_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('u'));  // uint16 - bjdata only\n            }\n            write_number(static_cast<std::uint16_t>(n), use_bjdata);\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n), use_bjdata);\n        }\n        else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint32_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('m'));  // uint32 - bjdata only\n            }\n            write_number(static_cast<std::uint32_t>(n), use_bjdata);\n        }\n        else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n), use_bjdata);\n        }\n        else if (use_bjdata && n <= (std::numeric_limits<uint64_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('M'));  // uint64 - bjdata only\n            }\n            write_number(static_cast<std::uint64_t>(n), use_bjdata);\n        }\n        else\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('H'));  // high-precision number\n            }\n\n            const auto number = BasicJsonType(n).dump();\n            write_number_with_ubjson_prefix(number.size(), true, use_bjdata);\n            for (std::size_t i = 0; i < number.size(); ++i)\n            {\n                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));\n            }\n        }\n    }\n\n    // UBJSON: write number (signed integer)\n    template < typename NumberType, typename std::enable_if <\n                   std::is_signed<NumberType>::value&&\n                   !std::is_floating_point<NumberType>::value, int >::type = 0 >\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix,\n                                         const bool use_bjdata)\n    {\n        if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<std::int8_t>(n), use_bjdata);\n        }\n        else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<std::uint8_t>(n), use_bjdata);\n        }\n        else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<std::int16_t>(n), use_bjdata);\n        }\n        else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::max)())))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('u'));  // uint16 - bjdata only\n            }\n            write_number(static_cast<uint16_t>(n), use_bjdata);\n        }\n        else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<std::int32_t>(n), use_bjdata);\n        }\n        else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::max)())))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('m'));  // uint32 - bjdata only\n            }\n            write_number(static_cast<uint32_t>(n), use_bjdata);\n        }\n        else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<std::int64_t>(n), use_bjdata);\n        }\n        // LCOV_EXCL_START\n        else\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('H'));  // high-precision number\n            }\n\n            const auto number = BasicJsonType(n).dump();\n            write_number_with_ubjson_prefix(number.size(), true, use_bjdata);\n            for (std::size_t i = 0; i < number.size(); ++i)\n            {\n                oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));\n            }\n        }\n        // LCOV_EXCL_STOP\n    }\n\n    /*!\n    @brief determine the type prefix of container values\n    */\n    CharType ubjson_prefix(const BasicJsonType& j, const bool use_bjdata) const noexcept\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n                return 'Z';\n\n            case value_t::boolean:\n                return j.m_value.boolean ? 'T' : 'F';\n\n            case value_t::number_integer:\n            {\n                if ((std::numeric_limits<std::int8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())\n                {\n                    return 'i';\n                }\n                if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())\n                {\n                    return 'U';\n                }\n                if ((std::numeric_limits<std::int16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())\n                {\n                    return 'I';\n                }\n                if (use_bjdata && ((std::numeric_limits<std::uint16_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)()))\n                {\n                    return 'u';\n                }\n                if ((std::numeric_limits<std::int32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())\n                {\n                    return 'l';\n                }\n                if (use_bjdata && ((std::numeric_limits<std::uint32_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)()))\n                {\n                    return 'm';\n                }\n                if ((std::numeric_limits<std::int64_t>::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())\n                {\n                    return 'L';\n                }\n                // anything else is treated as high-precision number\n                return 'H'; // LCOV_EXCL_LINE\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))\n                {\n                    return 'i';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))\n                {\n                    return 'U';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))\n                {\n                    return 'I';\n                }\n                if (use_bjdata && j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint16_t>::max)()))\n                {\n                    return 'u';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n                {\n                    return 'l';\n                }\n                if (use_bjdata && j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint32_t>::max)()))\n                {\n                    return 'm';\n                }\n                if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n                {\n                    return 'L';\n                }\n                if (use_bjdata && j.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())\n                {\n                    return 'M';\n                }\n                // anything else is treated as high-precision number\n                return 'H'; // LCOV_EXCL_LINE\n            }\n\n            case value_t::number_float:\n                return get_ubjson_float_prefix(j.m_value.number_float);\n\n            case value_t::string:\n                return 'S';\n\n            case value_t::array: // fallthrough\n            case value_t::binary:\n                return '[';\n\n            case value_t::object:\n                return '{';\n\n            case value_t::discarded:\n            default:  // discarded values\n                return 'N';\n        }\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(float /*unused*/)\n    {\n        return 'd';  // float 32\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(double /*unused*/)\n    {\n        return 'D';  // float 64\n    }\n\n    /*!\n    @return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid\n    */\n    bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type)\n    {\n        std::map<string_t, CharType> bjdtype = {{\"uint8\", 'U'},  {\"int8\", 'i'},  {\"uint16\", 'u'}, {\"int16\", 'I'},\n            {\"uint32\", 'm'}, {\"int32\", 'l'}, {\"uint64\", 'M'}, {\"int64\", 'L'}, {\"single\", 'd'}, {\"double\", 'D'}, {\"char\", 'C'}\n        };\n\n        string_t key = \"_ArrayType_\";\n        auto it = bjdtype.find(static_cast<string_t>(value.at(key)));\n        if (it == bjdtype.end())\n        {\n            return true;\n        }\n        CharType dtype = it->second;\n\n        key = \"_ArraySize_\";\n        std::size_t len = (value.at(key).empty() ? 0 : 1);\n        for (const auto& el : value.at(key))\n        {\n            len *= static_cast<std::size_t>(el.m_value.number_unsigned);\n        }\n\n        key = \"_ArrayData_\";\n        if (value.at(key).size() != len)\n        {\n            return true;\n        }\n\n        oa->write_character('[');\n        oa->write_character('$');\n        oa->write_character(dtype);\n        oa->write_character('#');\n\n        key = \"_ArraySize_\";\n        write_ubjson(value.at(key), use_count, use_type, true,  true);\n\n        key = \"_ArrayData_\";\n        if (dtype == 'U' || dtype == 'C')\n        {\n            for (const auto& el : value.at(key))\n            {\n                write_number(static_cast<std::uint8_t>(el.m_value.number_unsigned), true);\n            }\n        }\n        else if (dtype == 'i')\n        {\n            for (const auto& el : value.at(key))\n            {\n                write_number(static_cast<std::int8_t>(el.m_value.number_integer), true);\n            }\n        }\n        else if (dtype == 'u')\n        {\n            for (const auto& el : value.at(key))\n            {\n                write_number(static_cast<std::uint16_t>(el.m_value.number_unsigned), true);\n            }\n        }\n        else if (dtype == 'I')\n        {\n            for (const auto& el : value.at(key))\n            {\n                write_number(static_cast<std::int16_t>(el.m_value.number_integer), true);\n            }\n        }\n        else if (dtype == 'm')\n        {\n            for (const auto& el : value.at(key))\n            {\n                write_number(static_cast<std::uint32_t>(el.m_value.number_unsigned), true);\n            }\n        }\n        else if (dtype == 'l')\n        {\n            for (const auto& el : value.at(key))\n            {\n                write_number(static_cast<std::int32_t>(el.m_value.number_integer), true);\n            }\n        }\n        else if (dtype == 'M')\n        {\n            for (const auto& el : value.at(key))\n            {\n                write_number(static_cast<std::uint64_t>(el.m_value.number_unsigned), true);\n            }\n        }\n        else if (dtype == 'L')\n        {\n            for (const auto& el : value.at(key))\n            {\n                write_number(static_cast<std::int64_t>(el.m_value.number_integer), true);\n            }\n        }\n        else if (dtype == 'd')\n        {\n            for (const auto& el : value.at(key))\n            {\n                write_number(static_cast<float>(el.m_value.number_float), true);\n            }\n        }\n        else if (dtype == 'D')\n        {\n            for (const auto& el : value.at(key))\n            {\n                write_number(static_cast<double>(el.m_value.number_float), true);\n            }\n        }\n        return false;\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*\n    @brief write a number to output input\n    @param[in] n number of type @a NumberType\n    @param[in] OutputIsLittleEndian Set to true if output data is\n                                 required to be little endian\n    @tparam NumberType the type of the number\n\n    @note This function needs to respect the system's endianness, because bytes\n          in CBOR, MessagePack, and UBJSON are stored in network order (big\n          endian) and therefore need reordering on little endian systems.\n          On the other hand, BSON and BJData use little endian and should reorder\n          on big endian systems.\n    */\n    template<typename NumberType>\n    void write_number(const NumberType n, const bool OutputIsLittleEndian = false)\n    {\n        // step 1: write number to array of length NumberType\n        std::array<CharType, sizeof(NumberType)> vec{};\n        std::memcpy(vec.data(), &n, sizeof(NumberType));\n\n        // step 2: write array to output (with possible reordering)\n        if (is_little_endian != OutputIsLittleEndian)\n        {\n            // reverse byte order prior to conversion if necessary\n            std::reverse(vec.begin(), vec.end());\n        }\n\n        oa->write_characters(vec.data(), sizeof(NumberType));\n    }\n\n    void write_compact_float(const number_float_t n, detail::input_format_t format)\n    {\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n        if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&\n                static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&\n                static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))\n        {\n            oa->write_character(format == detail::input_format_t::cbor\n                                ? get_cbor_float_prefix(static_cast<float>(n))\n                                : get_msgpack_float_prefix(static_cast<float>(n)));\n            write_number(static_cast<float>(n));\n        }\n        else\n        {\n            oa->write_character(format == detail::input_format_t::cbor\n                                ? get_cbor_float_prefix(n)\n                                : get_msgpack_float_prefix(n));\n            write_number(n);\n        }\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n    }\n\n  public:\n    // The following to_char_type functions are implement the conversion\n    // between uint8_t and CharType. In case CharType is not unsigned,\n    // such a conversion is required to allow values greater than 128.\n    // See <https://github.com/nlohmann/json/issues/1286> for a discussion.\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return *reinterpret_cast<char*>(&x);\n    }\n\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >\n    static CharType to_char_type(std::uint8_t x) noexcept\n    {\n        static_assert(sizeof(std::uint8_t) == sizeof(CharType), \"size of CharType must be equal to std::uint8_t\");\n        static_assert(std::is_trivial<CharType>::value, \"CharType must be trivial\");\n        CharType result;\n        std::memcpy(&result, &x, sizeof(x));\n        return result;\n    }\n\n    template<typename C = CharType,\n             enable_if_t<std::is_unsigned<C>::value>* = nullptr>\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return x;\n    }\n\n    template < typename InputCharType, typename C = CharType,\n               enable_if_t <\n                   std::is_signed<C>::value &&\n                   std::is_signed<char>::value &&\n                   std::is_same<char, typename std::remove_cv<InputCharType>::type>::value\n                   > * = nullptr >\n    static constexpr CharType to_char_type(InputCharType x) noexcept\n    {\n        return x;\n    }\n\n  private:\n    /// whether we can assume little endianness\n    const bool is_little_endian = little_endianness();\n\n    /// the output\n    output_adapter_t<CharType> oa = nullptr;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n// #include <nlohmann/detail/output/serializer.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2008-2009 Björn Hoehrmann <bjoern@hoehrmann.de>\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <algorithm> // reverse, remove, fill, find, none_of\n#include <array> // array\n#include <clocale> // localeconv, lconv\n#include <cmath> // labs, isfinite, isnan, signbit\n#include <cstddef> // size_t, ptrdiff_t\n#include <cstdint> // uint8_t\n#include <cstdio> // snprintf\n#include <limits> // numeric_limits\n#include <string> // string, char_traits\n#include <iomanip> // setfill, setw\n#include <type_traits> // is_same\n#include <utility> // move\n\n// #include <nlohmann/detail/conversions/to_chars.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2009 Florian Loitsch <https://florian.loitsch.com/>\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <array> // array\n#include <cmath>   // signbit, isfinite\n#include <cstdint> // intN_t, uintN_t\n#include <cstring> // memcpy, memmove\n#include <limits> // numeric_limits\n#include <type_traits> // conditional\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n/*!\n@brief implements the Grisu2 algorithm for binary to decimal floating-point\nconversion.\n\nThis implementation is a slightly modified version of the reference\nimplementation which may be obtained from\nhttp://florian.loitsch.com/publications (bench.tar.gz).\n\nThe code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.\n\nFor a detailed description of the algorithm see:\n\n[1] Loitsch, \"Printing Floating-Point Numbers Quickly and Accurately with\n    Integers\", Proceedings of the ACM SIGPLAN 2010 Conference on Programming\n    Language Design and Implementation, PLDI 2010\n[2] Burger, Dybvig, \"Printing Floating-Point Numbers Quickly and Accurately\",\n    Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language\n    Design and Implementation, PLDI 1996\n*/\nnamespace dtoa_impl\n{\n\ntemplate<typename Target, typename Source>\nTarget reinterpret_bits(const Source source)\n{\n    static_assert(sizeof(Target) == sizeof(Source), \"size mismatch\");\n\n    Target target;\n    std::memcpy(&target, &source, sizeof(Source));\n    return target;\n}\n\nstruct diyfp // f * 2^e\n{\n    static constexpr int kPrecision = 64; // = q\n\n    std::uint64_t f = 0;\n    int e = 0;\n\n    constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}\n\n    /*!\n    @brief returns x - y\n    @pre x.e == y.e and x.f >= y.f\n    */\n    static diyfp sub(const diyfp& x, const diyfp& y) noexcept\n    {\n        JSON_ASSERT(x.e == y.e);\n        JSON_ASSERT(x.f >= y.f);\n\n        return {x.f - y.f, x.e};\n    }\n\n    /*!\n    @brief returns x * y\n    @note The result is rounded. (Only the upper q bits are returned.)\n    */\n    static diyfp mul(const diyfp& x, const diyfp& y) noexcept\n    {\n        static_assert(kPrecision == 64, \"internal error\");\n\n        // Computes:\n        //  f = round((x.f * y.f) / 2^q)\n        //  e = x.e + y.e + q\n\n        // Emulate the 64-bit * 64-bit multiplication:\n        //\n        // p = u * v\n        //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)\n        //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo         )) + 2^64 (u_hi v_hi         )\n        //   = (p0                ) + 2^32 ((p1                ) + (p2                )) + 2^64 (p3                )\n        //   = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                )\n        //   = (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo                      ) + 2^64 (p1_hi + p2_hi + p3)\n        //   = (p0_lo             ) + 2^32 (Q                                          ) + 2^64 (H                 )\n        //   = (p0_lo             ) + 2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H                 )\n        //\n        // (Since Q might be larger than 2^32 - 1)\n        //\n        //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)\n        //\n        // (Q_hi + H does not overflow a 64-bit int)\n        //\n        //   = p_lo + 2^64 p_hi\n\n        const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;\n        const std::uint64_t u_hi = x.f >> 32u;\n        const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;\n        const std::uint64_t v_hi = y.f >> 32u;\n\n        const std::uint64_t p0 = u_lo * v_lo;\n        const std::uint64_t p1 = u_lo * v_hi;\n        const std::uint64_t p2 = u_hi * v_lo;\n        const std::uint64_t p3 = u_hi * v_hi;\n\n        const std::uint64_t p0_hi = p0 >> 32u;\n        const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;\n        const std::uint64_t p1_hi = p1 >> 32u;\n        const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;\n        const std::uint64_t p2_hi = p2 >> 32u;\n\n        std::uint64_t Q = p0_hi + p1_lo + p2_lo;\n\n        // The full product might now be computed as\n        //\n        // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)\n        // p_lo = p0_lo + (Q << 32)\n        //\n        // But in this particular case here, the full p_lo is not required.\n        // Effectively we only need to add the highest bit in p_lo to p_hi (and\n        // Q_hi + 1 does not overflow).\n\n        Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up\n\n        const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);\n\n        return {h, x.e + y.e + 64};\n    }\n\n    /*!\n    @brief normalize x such that the significand is >= 2^(q-1)\n    @pre x.f != 0\n    */\n    static diyfp normalize(diyfp x) noexcept\n    {\n        JSON_ASSERT(x.f != 0);\n\n        while ((x.f >> 63u) == 0)\n        {\n            x.f <<= 1u;\n            x.e--;\n        }\n\n        return x;\n    }\n\n    /*!\n    @brief normalize x such that the result has the exponent E\n    @pre e >= x.e and the upper e - x.e bits of x.f must be zero.\n    */\n    static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept\n    {\n        const int delta = x.e - target_exponent;\n\n        JSON_ASSERT(delta >= 0);\n        JSON_ASSERT(((x.f << delta) >> delta) == x.f);\n\n        return {x.f << delta, target_exponent};\n    }\n};\n\nstruct boundaries\n{\n    diyfp w;\n    diyfp minus;\n    diyfp plus;\n};\n\n/*!\nCompute the (normalized) diyfp representing the input number 'value' and its\nboundaries.\n\n@pre value must be finite and positive\n*/\ntemplate<typename FloatType>\nboundaries compute_boundaries(FloatType value)\n{\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // Convert the IEEE representation into a diyfp.\n    //\n    // If v is denormal:\n    //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))\n    // If v is normalized:\n    //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))\n\n    static_assert(std::numeric_limits<FloatType>::is_iec559,\n                  \"internal error: dtoa_short requires an IEEE-754 floating-point implementation\");\n\n    constexpr int      kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)\n    constexpr int      kBias      = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);\n    constexpr int      kMinExp    = 1 - kBias;\n    constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)\n\n    using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;\n\n    const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value));\n    const std::uint64_t E = bits >> (kPrecision - 1);\n    const std::uint64_t F = bits & (kHiddenBit - 1);\n\n    const bool is_denormal = E == 0;\n    const diyfp v = is_denormal\n                    ? diyfp(F, kMinExp)\n                    : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);\n\n    // Compute the boundaries m- and m+ of the floating-point value\n    // v = f * 2^e.\n    //\n    // Determine v- and v+, the floating-point predecessor and successor if v,\n    // respectively.\n    //\n    //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)\n    //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)\n    //\n    //      v+ = v + 2^e\n    //\n    // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_\n    // between m- and m+ round to v, regardless of how the input rounding\n    // algorithm breaks ties.\n    //\n    //      ---+-------------+-------------+-------------+-------------+---  (A)\n    //         v-            m-            v             m+            v+\n    //\n    //      -----------------+------+------+-------------+-------------+---  (B)\n    //                       v-     m-     v             m+            v+\n\n    const bool lower_boundary_is_closer = F == 0 && E > 1;\n    const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);\n    const diyfp m_minus = lower_boundary_is_closer\n                          ? diyfp(4 * v.f - 1, v.e - 2)  // (B)\n                          : diyfp(2 * v.f - 1, v.e - 1); // (A)\n\n    // Determine the normalized w+ = m+.\n    const diyfp w_plus = diyfp::normalize(m_plus);\n\n    // Determine w- = m- such that e_(w-) = e_(w+).\n    const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);\n\n    return {diyfp::normalize(v), w_minus, w_plus};\n}\n\n// Given normalized diyfp w, Grisu needs to find a (normalized) cached\n// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies\n// within a certain range [alpha, gamma] (Definition 3.2 from [1])\n//\n//      alpha <= e = e_c + e_w + q <= gamma\n//\n// or\n//\n//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q\n//                          <= f_c * f_w * 2^gamma\n//\n// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies\n//\n//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma\n//\n// or\n//\n//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)\n//\n// The choice of (alpha,gamma) determines the size of the table and the form of\n// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well\n// in practice:\n//\n// The idea is to cut the number c * w = f * 2^e into two parts, which can be\n// processed independently: An integral part p1, and a fractional part p2:\n//\n//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e\n//              = (f div 2^-e) + (f mod 2^-e) * 2^e\n//              = p1 + p2 * 2^e\n//\n// The conversion of p1 into decimal form requires a series of divisions and\n// modulos by (a power of) 10. These operations are faster for 32-bit than for\n// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be\n// achieved by choosing\n//\n//      -e >= 32   or   e <= -32 := gamma\n//\n// In order to convert the fractional part\n//\n//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...\n//\n// into decimal form, the fraction is repeatedly multiplied by 10 and the digits\n// d[-i] are extracted in order:\n//\n//      (10 * p2) div 2^-e = d[-1]\n//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...\n//\n// The multiplication by 10 must not overflow. It is sufficient to choose\n//\n//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.\n//\n// Since p2 = f mod 2^-e < 2^-e,\n//\n//      -e <= 60   or   e >= -60 := alpha\n\nconstexpr int kAlpha = -60;\nconstexpr int kGamma = -32;\n\nstruct cached_power // c = f * 2^e ~= 10^k\n{\n    std::uint64_t f;\n    int e;\n    int k;\n};\n\n/*!\nFor a normalized diyfp w = f * 2^e, this function returns a (normalized) cached\npower-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c\nsatisfies (Definition 3.2 from [1])\n\n     alpha <= e_c + e + q <= gamma.\n*/\ninline cached_power get_cached_power_for_binary_exponent(int e)\n{\n    // Now\n    //\n    //      alpha <= e_c + e + q <= gamma                                    (1)\n    //      ==> f_c * 2^alpha <= c * 2^e * 2^q\n    //\n    // and since the c's are normalized, 2^(q-1) <= f_c,\n    //\n    //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)\n    //      ==> 2^(alpha - e - 1) <= c\n    //\n    // If c were an exact power of ten, i.e. c = 10^k, one may determine k as\n    //\n    //      k = ceil( log_10( 2^(alpha - e - 1) ) )\n    //        = ceil( (alpha - e - 1) * log_10(2) )\n    //\n    // From the paper:\n    // \"In theory the result of the procedure could be wrong since c is rounded,\n    //  and the computation itself is approximated [...]. In practice, however,\n    //  this simple function is sufficient.\"\n    //\n    // For IEEE double precision floating-point numbers converted into\n    // normalized diyfp's w = f * 2^e, with q = 64,\n    //\n    //      e >= -1022      (min IEEE exponent)\n    //           -52        (p - 1)\n    //           -52        (p - 1, possibly normalize denormal IEEE numbers)\n    //           -11        (normalize the diyfp)\n    //         = -1137\n    //\n    // and\n    //\n    //      e <= +1023      (max IEEE exponent)\n    //           -52        (p - 1)\n    //           -11        (normalize the diyfp)\n    //         = 960\n    //\n    // This binary exponent range [-1137,960] results in a decimal exponent\n    // range [-307,324]. One does not need to store a cached power for each\n    // k in this range. For each such k it suffices to find a cached power\n    // such that the exponent of the product lies in [alpha,gamma].\n    // This implies that the difference of the decimal exponents of adjacent\n    // table entries must be less than or equal to\n    //\n    //      floor( (gamma - alpha) * log_10(2) ) = 8.\n    //\n    // (A smaller distance gamma-alpha would require a larger table.)\n\n    // NB:\n    // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.\n\n    constexpr int kCachedPowersMinDecExp = -300;\n    constexpr int kCachedPowersDecStep = 8;\n\n    static constexpr std::array<cached_power, 79> kCachedPowers =\n    {\n        {\n            { 0xAB70FE17C79AC6CA, -1060, -300 },\n            { 0xFF77B1FCBEBCDC4F, -1034, -292 },\n            { 0xBE5691EF416BD60C, -1007, -284 },\n            { 0x8DD01FAD907FFC3C,  -980, -276 },\n            { 0xD3515C2831559A83,  -954, -268 },\n            { 0x9D71AC8FADA6C9B5,  -927, -260 },\n            { 0xEA9C227723EE8BCB,  -901, -252 },\n            { 0xAECC49914078536D,  -874, -244 },\n            { 0x823C12795DB6CE57,  -847, -236 },\n            { 0xC21094364DFB5637,  -821, -228 },\n            { 0x9096EA6F3848984F,  -794, -220 },\n            { 0xD77485CB25823AC7,  -768, -212 },\n            { 0xA086CFCD97BF97F4,  -741, -204 },\n            { 0xEF340A98172AACE5,  -715, -196 },\n            { 0xB23867FB2A35B28E,  -688, -188 },\n            { 0x84C8D4DFD2C63F3B,  -661, -180 },\n            { 0xC5DD44271AD3CDBA,  -635, -172 },\n            { 0x936B9FCEBB25C996,  -608, -164 },\n            { 0xDBAC6C247D62A584,  -582, -156 },\n            { 0xA3AB66580D5FDAF6,  -555, -148 },\n            { 0xF3E2F893DEC3F126,  -529, -140 },\n            { 0xB5B5ADA8AAFF80B8,  -502, -132 },\n            { 0x87625F056C7C4A8B,  -475, -124 },\n            { 0xC9BCFF6034C13053,  -449, -116 },\n            { 0x964E858C91BA2655,  -422, -108 },\n            { 0xDFF9772470297EBD,  -396, -100 },\n            { 0xA6DFBD9FB8E5B88F,  -369,  -92 },\n            { 0xF8A95FCF88747D94,  -343,  -84 },\n            { 0xB94470938FA89BCF,  -316,  -76 },\n            { 0x8A08F0F8BF0F156B,  -289,  -68 },\n            { 0xCDB02555653131B6,  -263,  -60 },\n            { 0x993FE2C6D07B7FAC,  -236,  -52 },\n            { 0xE45C10C42A2B3B06,  -210,  -44 },\n            { 0xAA242499697392D3,  -183,  -36 },\n            { 0xFD87B5F28300CA0E,  -157,  -28 },\n            { 0xBCE5086492111AEB,  -130,  -20 },\n            { 0x8CBCCC096F5088CC,  -103,  -12 },\n            { 0xD1B71758E219652C,   -77,   -4 },\n            { 0x9C40000000000000,   -50,    4 },\n            { 0xE8D4A51000000000,   -24,   12 },\n            { 0xAD78EBC5AC620000,     3,   20 },\n            { 0x813F3978F8940984,    30,   28 },\n            { 0xC097CE7BC90715B3,    56,   36 },\n            { 0x8F7E32CE7BEA5C70,    83,   44 },\n            { 0xD5D238A4ABE98068,   109,   52 },\n            { 0x9F4F2726179A2245,   136,   60 },\n            { 0xED63A231D4C4FB27,   162,   68 },\n            { 0xB0DE65388CC8ADA8,   189,   76 },\n            { 0x83C7088E1AAB65DB,   216,   84 },\n            { 0xC45D1DF942711D9A,   242,   92 },\n            { 0x924D692CA61BE758,   269,  100 },\n            { 0xDA01EE641A708DEA,   295,  108 },\n            { 0xA26DA3999AEF774A,   322,  116 },\n            { 0xF209787BB47D6B85,   348,  124 },\n            { 0xB454E4A179DD1877,   375,  132 },\n            { 0x865B86925B9BC5C2,   402,  140 },\n            { 0xC83553C5C8965D3D,   428,  148 },\n            { 0x952AB45CFA97A0B3,   455,  156 },\n            { 0xDE469FBD99A05FE3,   481,  164 },\n            { 0xA59BC234DB398C25,   508,  172 },\n            { 0xF6C69A72A3989F5C,   534,  180 },\n            { 0xB7DCBF5354E9BECE,   561,  188 },\n            { 0x88FCF317F22241E2,   588,  196 },\n            { 0xCC20CE9BD35C78A5,   614,  204 },\n            { 0x98165AF37B2153DF,   641,  212 },\n            { 0xE2A0B5DC971F303A,   667,  220 },\n            { 0xA8D9D1535CE3B396,   694,  228 },\n            { 0xFB9B7CD9A4A7443C,   720,  236 },\n            { 0xBB764C4CA7A44410,   747,  244 },\n            { 0x8BAB8EEFB6409C1A,   774,  252 },\n            { 0xD01FEF10A657842C,   800,  260 },\n            { 0x9B10A4E5E9913129,   827,  268 },\n            { 0xE7109BFBA19C0C9D,   853,  276 },\n            { 0xAC2820D9623BF429,   880,  284 },\n            { 0x80444B5E7AA7CF85,   907,  292 },\n            { 0xBF21E44003ACDD2D,   933,  300 },\n            { 0x8E679C2F5E44FF8F,   960,  308 },\n            { 0xD433179D9C8CB841,   986,  316 },\n            { 0x9E19DB92B4E31BA9,  1013,  324 },\n        }\n    };\n\n    // This computation gives exactly the same results for k as\n    //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)\n    // for |e| <= 1500, but doesn't require floating-point operations.\n    // NB: log_10(2) ~= 78913 / 2^18\n    JSON_ASSERT(e >= -1500);\n    JSON_ASSERT(e <=  1500);\n    const int f = kAlpha - e - 1;\n    const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);\n\n    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;\n    JSON_ASSERT(index >= 0);\n    JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());\n\n    const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];\n    JSON_ASSERT(kAlpha <= cached.e + e + 64);\n    JSON_ASSERT(kGamma >= cached.e + e + 64);\n\n    return cached;\n}\n\n/*!\nFor n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.\nFor n == 0, returns 1 and sets pow10 := 1.\n*/\ninline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)\n{\n    // LCOV_EXCL_START\n    if (n >= 1000000000)\n    {\n        pow10 = 1000000000;\n        return 10;\n    }\n    // LCOV_EXCL_STOP\n    if (n >= 100000000)\n    {\n        pow10 = 100000000;\n        return  9;\n    }\n    if (n >= 10000000)\n    {\n        pow10 = 10000000;\n        return  8;\n    }\n    if (n >= 1000000)\n    {\n        pow10 = 1000000;\n        return  7;\n    }\n    if (n >= 100000)\n    {\n        pow10 = 100000;\n        return  6;\n    }\n    if (n >= 10000)\n    {\n        pow10 = 10000;\n        return  5;\n    }\n    if (n >= 1000)\n    {\n        pow10 = 1000;\n        return  4;\n    }\n    if (n >= 100)\n    {\n        pow10 = 100;\n        return  3;\n    }\n    if (n >= 10)\n    {\n        pow10 = 10;\n        return  2;\n    }\n\n    pow10 = 1;\n    return 1;\n}\n\ninline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,\n                         std::uint64_t rest, std::uint64_t ten_k)\n{\n    JSON_ASSERT(len >= 1);\n    JSON_ASSERT(dist <= delta);\n    JSON_ASSERT(rest <= delta);\n    JSON_ASSERT(ten_k > 0);\n\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    //                                  ten_k\n    //                                <------>\n    //                                       <---- rest ---->\n    // --------------[------------------+----+--------------]--------------\n    //                                  w    V\n    //                                       = buf * 10^k\n    //\n    // ten_k represents a unit-in-the-last-place in the decimal representation\n    // stored in buf.\n    // Decrement buf by ten_k while this takes buf closer to w.\n\n    // The tests are written in this order to avoid overflow in unsigned\n    // integer arithmetic.\n\n    while (rest < dist\n            && delta - rest >= ten_k\n            && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))\n    {\n        JSON_ASSERT(buf[len - 1] != '0');\n        buf[len - 1]--;\n        rest += ten_k;\n    }\n}\n\n/*!\nGenerates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.\nM- and M+ must be normalized and share the same exponent -60 <= e <= -32.\n*/\ninline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,\n                             diyfp M_minus, diyfp w, diyfp M_plus)\n{\n    static_assert(kAlpha >= -60, \"internal error\");\n    static_assert(kGamma <= -32, \"internal error\");\n\n    // Generates the digits (and the exponent) of a decimal floating-point\n    // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's\n    // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.\n    //\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    // Grisu2 generates the digits of M+ from left to right and stops as soon as\n    // V is in [M-,M+].\n\n    JSON_ASSERT(M_plus.e >= kAlpha);\n    JSON_ASSERT(M_plus.e <= kGamma);\n\n    std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)\n    std::uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)\n\n    // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):\n    //\n    //      M+ = f * 2^e\n    //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e\n    //         = ((p1        ) * 2^-e + (p2        )) * 2^e\n    //         = p1 + p2 * 2^e\n\n    const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);\n\n    auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)\n    std::uint64_t p2 = M_plus.f & (one.f - 1);                    // p2 = f mod 2^-e\n\n    // 1)\n    //\n    // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]\n\n    JSON_ASSERT(p1 > 0);\n\n    std::uint32_t pow10{};\n    const int k = find_largest_pow10(p1, pow10);\n\n    //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)\n    //\n    //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))\n    //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))\n    //\n    //      M+ = p1                                             + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e\n    //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e\n    //\n    // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)\n    //\n    //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]\n    //\n    // but stop as soon as\n    //\n    //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e\n\n    int n = k;\n    while (n > 0)\n    {\n        // Invariants:\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        //\n        const std::uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)\n        const std::uint32_t r = p1 % pow10;  // r = p1 mod 10^(n-1)\n        //\n        //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e\n        //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)\n        //\n        p1 = r;\n        n--;\n        //\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)\n        //      pow10 = 10^n\n        //\n\n        // Now check if enough digits have been generated.\n        // Compute\n        //\n        //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e\n        //\n        // Note:\n        // Since rest and delta share the same exponent e, it suffices to\n        // compare the significands.\n        const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;\n        if (rest <= delta)\n        {\n            // V = buffer * 10^n, with M- <= V <= M+.\n\n            decimal_exponent += n;\n\n            // We may now just stop. But instead look if the buffer could be\n            // decremented to bring V closer to w.\n            //\n            // pow10 = 10^n is now 1 ulp in the decimal representation V.\n            // The rounding procedure works with diyfp's with an implicit\n            // exponent of e.\n            //\n            //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e\n            //\n            const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;\n            grisu2_round(buffer, length, dist, delta, rest, ten_n);\n\n            return;\n        }\n\n        pow10 /= 10;\n        //\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        // Invariants restored.\n    }\n\n    // 2)\n    //\n    // The digits of the integral part have been generated:\n    //\n    //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e\n    //         = buffer            + p2 * 2^e\n    //\n    // Now generate the digits of the fractional part p2 * 2^e.\n    //\n    // Note:\n    // No decimal point is generated: the exponent is adjusted instead.\n    //\n    // p2 actually represents the fraction\n    //\n    //      p2 * 2^e\n    //          = p2 / 2^-e\n    //          = d[-1] / 10^1 + d[-2] / 10^2 + ...\n    //\n    // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)\n    //\n    //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m\n    //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)\n    //\n    // using\n    //\n    //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)\n    //                = (                   d) * 2^-e + (                   r)\n    //\n    // or\n    //      10^m * p2 * 2^e = d + r * 2^e\n    //\n    // i.e.\n    //\n    //      M+ = buffer + p2 * 2^e\n    //         = buffer + 10^-m * (d + r * 2^e)\n    //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e\n    //\n    // and stop as soon as 10^-m * r * 2^e <= delta * 2^e\n\n    JSON_ASSERT(p2 > delta);\n\n    int m = 0;\n    for (;;)\n    {\n        // Invariant:\n        //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e\n        //         = buffer * 10^-m + 10^-m * (p2                                 ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e\n        //\n        JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);\n        p2 *= 10;\n        const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e\n        const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e\n        //\n        //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))\n        //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        JSON_ASSERT(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        p2 = r;\n        m++;\n        //\n        //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e\n        // Invariant restored.\n\n        // Check if enough digits have been generated.\n        //\n        //      10^-m * p2 * 2^e <= delta * 2^e\n        //              p2 * 2^e <= 10^m * delta * 2^e\n        //                    p2 <= 10^m * delta\n        delta *= 10;\n        dist  *= 10;\n        if (p2 <= delta)\n        {\n            break;\n        }\n    }\n\n    // V = buffer * 10^-m, with M- <= V <= M+.\n\n    decimal_exponent -= m;\n\n    // 1 ulp in the decimal representation is now 10^-m.\n    // Since delta and dist are now scaled by 10^m, we need to do the\n    // same with ulp in order to keep the units in sync.\n    //\n    //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e\n    //\n    const std::uint64_t ten_m = one.f;\n    grisu2_round(buffer, length, dist, delta, p2, ten_m);\n\n    // By construction this algorithm generates the shortest possible decimal\n    // number (Loitsch, Theorem 6.2) which rounds back to w.\n    // For an input number of precision p, at least\n    //\n    //      N = 1 + ceil(p * log_10(2))\n    //\n    // decimal digits are sufficient to identify all binary floating-point\n    // numbers (Matula, \"In-and-Out conversions\").\n    // This implies that the algorithm does not produce more than N decimal\n    // digits.\n    //\n    //      N = 17 for p = 53 (IEEE double precision)\n    //      N = 9  for p = 24 (IEEE single precision)\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\nJSON_HEDLEY_NON_NULL(1)\ninline void grisu2(char* buf, int& len, int& decimal_exponent,\n                   diyfp m_minus, diyfp v, diyfp m_plus)\n{\n    JSON_ASSERT(m_plus.e == m_minus.e);\n    JSON_ASSERT(m_plus.e == v.e);\n\n    //  --------(-----------------------+-----------------------)--------    (A)\n    //          m-                      v                       m+\n    //\n    //  --------------------(-----------+-----------------------)--------    (B)\n    //                      m-          v                       m+\n    //\n    // First scale v (and m- and m+) such that the exponent is in the range\n    // [alpha, gamma].\n\n    const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);\n\n    const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k\n\n    // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]\n    const diyfp w       = diyfp::mul(v,       c_minus_k);\n    const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);\n    const diyfp w_plus  = diyfp::mul(m_plus,  c_minus_k);\n\n    //  ----(---+---)---------------(---+---)---------------(---+---)----\n    //          w-                      w                       w+\n    //          = c*m-                  = c*v                   = c*m+\n    //\n    // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and\n    // w+ are now off by a small amount.\n    // In fact:\n    //\n    //      w - v * 10^k < 1 ulp\n    //\n    // To account for this inaccuracy, add resp. subtract 1 ulp.\n    //\n    //  --------+---[---------------(---+---)---------------]---+--------\n    //          w-  M-                  w                   M+  w+\n    //\n    // Now any number in [M-, M+] (bounds included) will round to w when input,\n    // regardless of how the input rounding algorithm breaks ties.\n    //\n    // And digit_gen generates the shortest possible such number in [M-, M+].\n    // Note that this does not mean that Grisu2 always generates the shortest\n    // possible number in the interval (m-, m+).\n    const diyfp M_minus(w_minus.f + 1, w_minus.e);\n    const diyfp M_plus (w_plus.f  - 1, w_plus.e );\n\n    decimal_exponent = -cached.k; // = -(-k) = k\n\n    grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1)\nvoid grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)\n{\n    static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,\n                  \"internal error: not enough precision\");\n\n    JSON_ASSERT(std::isfinite(value));\n    JSON_ASSERT(value > 0);\n\n    // If the neighbors (and boundaries) of 'value' are always computed for double-precision\n    // numbers, all float's can be recovered using strtod (and strtof). However, the resulting\n    // decimal representations are not exactly \"short\".\n    //\n    // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)\n    // says \"value is converted to a string as if by std::sprintf in the default (\"C\") locale\"\n    // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars'\n    // does.\n    // On the other hand, the documentation for 'std::to_chars' requires that \"parsing the\n    // representation using the corresponding std::from_chars function recovers value exactly\". That\n    // indicates that single precision floating-point numbers should be recovered using\n    // 'std::strtof'.\n    //\n    // NB: If the neighbors are computed for single-precision numbers, there is a single float\n    //     (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision\n    //     value is off by 1 ulp.\n#if 0\n    const boundaries w = compute_boundaries(static_cast<double>(value));\n#else\n    const boundaries w = compute_boundaries(value);\n#endif\n\n    grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);\n}\n\n/*!\n@brief appends a decimal representation of e to buf\n@return a pointer to the element following the exponent.\n@pre -1000 < e < 1000\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* append_exponent(char* buf, int e)\n{\n    JSON_ASSERT(e > -1000);\n    JSON_ASSERT(e <  1000);\n\n    if (e < 0)\n    {\n        e = -e;\n        *buf++ = '-';\n    }\n    else\n    {\n        *buf++ = '+';\n    }\n\n    auto k = static_cast<std::uint32_t>(e);\n    if (k < 10)\n    {\n        // Always print at least two digits in the exponent.\n        // This is for compatibility with printf(\"%g\").\n        *buf++ = '0';\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else if (k < 100)\n    {\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else\n    {\n        *buf++ = static_cast<char>('0' + k / 100);\n        k %= 100;\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n\n    return buf;\n}\n\n/*!\n@brief prettify v = buf * 10^decimal_exponent\n\nIf v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point\nnotation. Otherwise it will be printed in exponential notation.\n\n@pre min_exp < 0\n@pre max_exp > 0\n*/\nJSON_HEDLEY_NON_NULL(1)\nJSON_HEDLEY_RETURNS_NON_NULL\ninline char* format_buffer(char* buf, int len, int decimal_exponent,\n                           int min_exp, int max_exp)\n{\n    JSON_ASSERT(min_exp < 0);\n    JSON_ASSERT(max_exp > 0);\n\n    const int k = len;\n    const int n = len + decimal_exponent;\n\n    // v = buf * 10^(n-k)\n    // k is the length of the buffer (number of decimal digits)\n    // n is the position of the decimal point relative to the start of the buffer.\n\n    if (k <= n && n <= max_exp)\n    {\n        // digits[000]\n        // len <= max_exp + 2\n\n        std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));\n        // Make it look like a floating-point number (#362, #378)\n        buf[n + 0] = '.';\n        buf[n + 1] = '0';\n        return buf + (static_cast<size_t>(n) + 2);\n    }\n\n    if (0 < n && n <= max_exp)\n    {\n        // dig.its\n        // len <= max_digits10 + 1\n\n        JSON_ASSERT(k > n);\n\n        std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));\n        buf[n] = '.';\n        return buf + (static_cast<size_t>(k) + 1U);\n    }\n\n    if (min_exp < n && n <= 0)\n    {\n        // 0.[000]digits\n        // len <= 2 + (-min_exp - 1) + max_digits10\n\n        std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k));\n        buf[0] = '0';\n        buf[1] = '.';\n        std::memset(buf + 2, '0', static_cast<size_t>(-n));\n        return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));\n    }\n\n    if (k == 1)\n    {\n        // dE+123\n        // len <= 1 + 5\n\n        buf += 1;\n    }\n    else\n    {\n        // d.igitsE+123\n        // len <= max_digits10 + 1 + 5\n\n        std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);\n        buf[1] = '.';\n        buf += 1 + static_cast<size_t>(k);\n    }\n\n    *buf++ = 'e';\n    return append_exponent(buf, n - 1);\n}\n\n}  // namespace dtoa_impl\n\n/*!\n@brief generates a decimal representation of the floating-point number value in [first, last).\n\nThe format of the resulting decimal representation is similar to printf's %g\nformat. Returns an iterator pointing past-the-end of the decimal representation.\n\n@note The input number must be finite, i.e. NaN's and Inf's are not supported.\n@note The buffer must be large enough.\n@note The result is NOT null-terminated.\n*/\ntemplate<typename FloatType>\nJSON_HEDLEY_NON_NULL(1, 2)\nJSON_HEDLEY_RETURNS_NON_NULL\nchar* to_chars(char* first, const char* last, FloatType value)\n{\n    static_cast<void>(last); // maybe unused - fix warning\n    JSON_ASSERT(std::isfinite(value));\n\n    // Use signbit(value) instead of (value < 0) since signbit works for -0.\n    if (std::signbit(value))\n    {\n        value = -value;\n        *first++ = '-';\n    }\n\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n    if (value == 0) // +-0\n    {\n        *first++ = '0';\n        // Make it look like a floating-point number (#362, #378)\n        *first++ = '.';\n        *first++ = '0';\n        return first;\n    }\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);\n\n    // Compute v = buffer * 10^decimal_exponent.\n    // The decimal digits are stored in the buffer, which needs to be interpreted\n    // as an unsigned decimal integer.\n    // len is the length of the buffer, i.e. the number of decimal digits.\n    int len = 0;\n    int decimal_exponent = 0;\n    dtoa_impl::grisu2(first, len, decimal_exponent, value);\n\n    JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);\n\n    // Format the buffer like printf(\"%.*g\", prec, value)\n    constexpr int kMinExp = -4;\n    // Use digits10 here to increase compatibility with version 2.\n    constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;\n\n    JSON_ASSERT(last - first >= kMaxExp + 2);\n    JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);\n    JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);\n\n    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);\n}\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n// #include <nlohmann/detail/string_concat.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\nnamespace detail\n{\n\n///////////////////\n// serialization //\n///////////////////\n\n/// how to treat decoding errors\nenum class error_handler_t\n{\n    strict,  ///< throw a type_error exception in case of invalid UTF-8\n    replace, ///< replace invalid UTF-8 sequences with U+FFFD\n    ignore   ///< ignore invalid UTF-8 sequences\n};\n\ntemplate<typename BasicJsonType>\nclass serializer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using binary_char_t = typename BasicJsonType::binary_t::value_type;\n    static constexpr std::uint8_t UTF8_ACCEPT = 0;\n    static constexpr std::uint8_t UTF8_REJECT = 1;\n\n  public:\n    /*!\n    @param[in] s  output stream to serialize to\n    @param[in] ichar  indentation character to use\n    @param[in] error_handler_  how to react on decoding errors\n    */\n    serializer(output_adapter_t<char> s, const char ichar,\n               error_handler_t error_handler_ = error_handler_t::strict)\n        : o(std::move(s))\n        , loc(std::localeconv())\n        , thousands_sep(loc->thousands_sep == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))\n        , decimal_point(loc->decimal_point == nullptr ? '\\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))\n        , indent_char(ichar)\n        , indent_string(512, indent_char)\n        , error_handler(error_handler_)\n    {}\n\n    // delete because of pointer members\n    serializer(const serializer&) = delete;\n    serializer& operator=(const serializer&) = delete;\n    serializer(serializer&&) = delete;\n    serializer& operator=(serializer&&) = delete;\n    ~serializer() = default;\n\n    /*!\n    @brief internal implementation of the serialization function\n\n    This function is called by the public member function dump and organizes\n    the serialization internally. The indentation level is propagated as\n    additional parameter. In case of arrays and objects, the function is\n    called recursively.\n\n    - strings and object keys are escaped using `escape_string()`\n    - integer numbers are converted implicitly via `operator<<`\n    - floating-point numbers are converted to a string using `\"%g\"` format\n    - binary values are serialized as objects containing the subtype and the\n      byte array\n\n    @param[in] val               value to serialize\n    @param[in] pretty_print      whether the output shall be pretty-printed\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] indent_step       the indent level\n    @param[in] current_indent    the current indent level (only used internally)\n    */\n    void dump(const BasicJsonType& val,\n              const bool pretty_print,\n              const bool ensure_ascii,\n              const unsigned int indent_step,\n              const unsigned int current_indent = 0)\n    {\n        switch (val.m_type)\n        {\n            case value_t::object:\n            {\n                if (val.m_value.object->empty())\n                {\n                    o->write_characters(\"{}\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\": \", 3);\n                        dump(i->second, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\": \", 3);\n                    dump(i->second, true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_character('{');\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\":\", 2);\n                        dump(i->second, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(i != val.m_value.object->cend());\n                    JSON_ASSERT(std::next(i) == val.m_value.object->cend());\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\":\", 2);\n                    dump(i->second, false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character('}');\n                }\n\n                return;\n            }\n\n            case value_t::array:\n            {\n                if (val.m_value.array->empty())\n                {\n                    o->write_characters(\"[]\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"[\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        dump(*i, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_value.array->empty());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character(']');\n                }\n                else\n                {\n                    o->write_character('[');\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        dump(*i, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    JSON_ASSERT(!val.m_value.array->empty());\n                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character(']');\n                }\n\n                return;\n            }\n\n            case value_t::string:\n            {\n                o->write_character('\\\"');\n                dump_escaped(*val.m_value.string, ensure_ascii);\n                o->write_character('\\\"');\n                return;\n            }\n\n            case value_t::binary:\n            {\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"bytes\\\": [\", 10);\n\n                    if (!val.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_value.binary->cbegin();\n                                i != val.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_characters(\", \", 2);\n                        }\n                        dump_integer(val.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\n\", 3);\n                    o->write_characters(indent_string.c_str(), new_indent);\n\n                    o->write_characters(\"\\\"subtype\\\": \", 11);\n                    if (val.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_value.binary->subtype());\n                    }\n                    else\n                    {\n                        o->write_characters(\"null\", 4);\n                    }\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_characters(\"{\\\"bytes\\\":[\", 10);\n\n                    if (!val.m_value.binary->empty())\n                    {\n                        for (auto i = val.m_value.binary->cbegin();\n                                i != val.m_value.binary->cend() - 1; ++i)\n                        {\n                            dump_integer(*i);\n                            o->write_character(',');\n                        }\n                        dump_integer(val.m_value.binary->back());\n                    }\n\n                    o->write_characters(\"],\\\"subtype\\\":\", 12);\n                    if (val.m_value.binary->has_subtype())\n                    {\n                        dump_integer(val.m_value.binary->subtype());\n                        o->write_character('}');\n                    }\n                    else\n                    {\n                        o->write_characters(\"null}\", 5);\n                    }\n                }\n                return;\n            }\n\n            case value_t::boolean:\n            {\n                if (val.m_value.boolean)\n                {\n                    o->write_characters(\"true\", 4);\n                }\n                else\n                {\n                    o->write_characters(\"false\", 5);\n                }\n                return;\n            }\n\n            case value_t::number_integer:\n            {\n                dump_integer(val.m_value.number_integer);\n                return;\n            }\n\n            case value_t::number_unsigned:\n            {\n                dump_integer(val.m_value.number_unsigned);\n                return;\n            }\n\n            case value_t::number_float:\n            {\n                dump_float(val.m_value.number_float);\n                return;\n            }\n\n            case value_t::discarded:\n            {\n                o->write_characters(\"<discarded>\", 11);\n                return;\n            }\n\n            case value_t::null:\n            {\n                o->write_characters(\"null\", 4);\n                return;\n            }\n\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n    }\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief dump escaped string\n\n    Escape a string by replacing certain special characters by a sequence of an\n    escape character (backslash) and another character and other control\n    characters by a sequence of \"\\u\" followed by a four-digit hex\n    representation. The escaped string is written to output stream @a o.\n\n    @param[in] s  the string to escape\n    @param[in] ensure_ascii  whether to escape non-ASCII characters with\n                             \\uXXXX sequences\n\n    @complexity Linear in the length of string @a s.\n    */\n    void dump_escaped(const string_t& s, const bool ensure_ascii)\n    {\n        std::uint32_t codepoint{};\n        std::uint8_t state = UTF8_ACCEPT;\n        std::size_t bytes = 0;  // number of bytes written to string_buffer\n\n        // number of bytes written at the point of the last valid byte\n        std::size_t bytes_after_last_accept = 0;\n        std::size_t undumped_chars = 0;\n\n        for (std::size_t i = 0; i < s.size(); ++i)\n        {\n            const auto byte = static_cast<std::uint8_t>(s[i]);\n\n            switch (decode(state, codepoint, byte))\n            {\n                case UTF8_ACCEPT:  // decode found a new code point\n                {\n                    switch (codepoint)\n                    {\n                        case 0x08: // backspace\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'b';\n                            break;\n                        }\n\n                        case 0x09: // horizontal tab\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 't';\n                            break;\n                        }\n\n                        case 0x0A: // newline\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'n';\n                            break;\n                        }\n\n                        case 0x0C: // formfeed\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'f';\n                            break;\n                        }\n\n                        case 0x0D: // carriage return\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'r';\n                            break;\n                        }\n\n                        case 0x22: // quotation mark\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\"';\n                            break;\n                        }\n\n                        case 0x5C: // reverse solidus\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\\';\n                            break;\n                        }\n\n                        default:\n                        {\n                            // escape control characters (0x00..0x1F) or, if\n                            // ensure_ascii parameter is used, non-ASCII characters\n                            if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))\n                            {\n                                if (codepoint <= 0xFFFF)\n                                {\n                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                                    static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, \"\\\\u%04x\",\n                                                                      static_cast<std::uint16_t>(codepoint)));\n                                    bytes += 6;\n                                }\n                                else\n                                {\n                                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n                                    static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, \"\\\\u%04x\\\\u%04x\",\n                                                                      static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),\n                                                                      static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));\n                                    bytes += 12;\n                                }\n                            }\n                            else\n                            {\n                                // copy byte to buffer (all previous bytes\n                                // been copied have in default case above)\n                                string_buffer[bytes++] = s[i];\n                            }\n                            break;\n                        }\n                    }\n\n                    // write buffer and reset index; there must be 13 bytes\n                    // left, as this is the maximal number of bytes to be\n                    // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                    if (string_buffer.size() - bytes < 13)\n                    {\n                        o->write_characters(string_buffer.data(), bytes);\n                        bytes = 0;\n                    }\n\n                    // remember the byte position of this accept\n                    bytes_after_last_accept = bytes;\n                    undumped_chars = 0;\n                    break;\n                }\n\n                case UTF8_REJECT:  // decode found invalid UTF-8 byte\n                {\n                    switch (error_handler)\n                    {\n                        case error_handler_t::strict:\n                        {\n                            JSON_THROW(type_error::create(316, concat(\"invalid UTF-8 byte at index \", std::to_string(i), \": 0x\", hex_bytes(byte | 0)), nullptr));\n                        }\n\n                        case error_handler_t::ignore:\n                        case error_handler_t::replace:\n                        {\n                            // in case we saw this character the first time, we\n                            // would like to read it again, because the byte\n                            // may be OK for itself, but just not OK for the\n                            // previous sequence\n                            if (undumped_chars > 0)\n                            {\n                                --i;\n                            }\n\n                            // reset length buffer to the last accepted index;\n                            // thus removing/ignoring the invalid characters\n                            bytes = bytes_after_last_accept;\n\n                            if (error_handler == error_handler_t::replace)\n                            {\n                                // add a replacement character\n                                if (ensure_ascii)\n                                {\n                                    string_buffer[bytes++] = '\\\\';\n                                    string_buffer[bytes++] = 'u';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'd';\n                                }\n                                else\n                                {\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xEF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBD');\n                                }\n\n                                // write buffer and reset index; there must be 13 bytes\n                                // left, as this is the maximal number of bytes to be\n                                // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                                if (string_buffer.size() - bytes < 13)\n                                {\n                                    o->write_characters(string_buffer.data(), bytes);\n                                    bytes = 0;\n                                }\n\n                                bytes_after_last_accept = bytes;\n                            }\n\n                            undumped_chars = 0;\n\n                            // continue processing the string\n                            state = UTF8_ACCEPT;\n                            break;\n                        }\n\n                        default:            // LCOV_EXCL_LINE\n                            JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n\n                default:  // decode found yet incomplete multi-byte code point\n                {\n                    if (!ensure_ascii)\n                    {\n                        // code point will not be escaped - copy byte to buffer\n                        string_buffer[bytes++] = s[i];\n                    }\n                    ++undumped_chars;\n                    break;\n                }\n            }\n        }\n\n        // we finished processing the string\n        if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))\n        {\n            // write buffer\n            if (bytes > 0)\n            {\n                o->write_characters(string_buffer.data(), bytes);\n            }\n        }\n        else\n        {\n            // we finish reading, but do not accept: string was incomplete\n            switch (error_handler)\n            {\n                case error_handler_t::strict:\n                {\n                    JSON_THROW(type_error::create(316, concat(\"incomplete UTF-8 string; last byte: 0x\", hex_bytes(static_cast<std::uint8_t>(s.back() | 0))), nullptr));\n                }\n\n                case error_handler_t::ignore:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    break;\n                }\n\n                case error_handler_t::replace:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    // add a replacement character\n                    if (ensure_ascii)\n                    {\n                        o->write_characters(\"\\\\ufffd\", 6);\n                    }\n                    else\n                    {\n                        o->write_characters(\"\\xEF\\xBF\\xBD\", 3);\n                    }\n                    break;\n                }\n\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            }\n        }\n    }\n\n  private:\n    /*!\n    @brief count digits\n\n    Count the number of decimal (base 10) digits for an input unsigned integer.\n\n    @param[in] x  unsigned integer number to count its digits\n    @return    number of decimal digits\n    */\n    inline unsigned int count_digits(number_unsigned_t x) noexcept\n    {\n        unsigned int n_digits = 1;\n        for (;;)\n        {\n            if (x < 10)\n            {\n                return n_digits;\n            }\n            if (x < 100)\n            {\n                return n_digits + 1;\n            }\n            if (x < 1000)\n            {\n                return n_digits + 2;\n            }\n            if (x < 10000)\n            {\n                return n_digits + 3;\n            }\n            x = x / 10000u;\n            n_digits += 4;\n        }\n    }\n\n    /*!\n     * @brief convert a byte to a uppercase hex representation\n     * @param[in] byte byte to represent\n     * @return representation (\"00\"..\"FF\")\n     */\n    static std::string hex_bytes(std::uint8_t byte)\n    {\n        std::string result = \"FF\";\n        constexpr const char* nibble_to_hex = \"0123456789ABCDEF\";\n        result[0] = nibble_to_hex[byte / 16];\n        result[1] = nibble_to_hex[byte % 16];\n        return result;\n    }\n\n    // templates to avoid warnings about useless casts\n    template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>\n    bool is_negative_number(NumberType x)\n    {\n        return x < 0;\n    }\n\n    template < typename NumberType, enable_if_t <std::is_unsigned<NumberType>::value, int > = 0 >\n    bool is_negative_number(NumberType /*unused*/)\n    {\n        return false;\n    }\n\n    /*!\n    @brief dump an integer\n\n    Dump a given integer to output stream @a o. Works internally with\n    @a number_buffer.\n\n    @param[in] x  integer number (signed or unsigned) to dump\n    @tparam NumberType either @a number_integer_t or @a number_unsigned_t\n    */\n    template < typename NumberType, detail::enable_if_t <\n                   std::is_integral<NumberType>::value ||\n                   std::is_same<NumberType, number_unsigned_t>::value ||\n                   std::is_same<NumberType, number_integer_t>::value ||\n                   std::is_same<NumberType, binary_char_t>::value,\n                   int > = 0 >\n    void dump_integer(NumberType x)\n    {\n        static constexpr std::array<std::array<char, 2>, 100> digits_to_99\n        {\n            {\n                {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},\n                {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},\n                {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},\n                {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},\n                {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},\n                {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},\n                {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},\n                {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},\n                {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},\n                {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},\n            }\n        };\n\n        // special case for \"0\"\n        if (x == 0)\n        {\n            o->write_character('0');\n            return;\n        }\n\n        // use a pointer to fill the buffer\n        auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n\n        number_unsigned_t abs_value;\n\n        unsigned int n_chars{};\n\n        if (is_negative_number(x))\n        {\n            *buffer_ptr = '-';\n            abs_value = remove_sign(static_cast<number_integer_t>(x));\n\n            // account one more byte for the minus sign\n            n_chars = 1 + count_digits(abs_value);\n        }\n        else\n        {\n            abs_value = static_cast<number_unsigned_t>(x);\n            n_chars = count_digits(abs_value);\n        }\n\n        // spare 1 byte for '\\0'\n        JSON_ASSERT(n_chars < number_buffer.size() - 1);\n\n        // jump to the end to generate the string from backward,\n        // so we later avoid reversing the result\n        buffer_ptr += n_chars;\n\n        // Fast int2ascii implementation inspired by \"Fastware\" talk by Andrei Alexandrescu\n        // See: https://www.youtube.com/watch?v=o4-CwDo2zpg\n        while (abs_value >= 100)\n        {\n            const auto digits_index = static_cast<unsigned>((abs_value % 100));\n            abs_value /= 100;\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n\n        if (abs_value >= 10)\n        {\n            const auto digits_index = static_cast<unsigned>(abs_value);\n            *(--buffer_ptr) = digits_to_99[digits_index][1];\n            *(--buffer_ptr) = digits_to_99[digits_index][0];\n        }\n        else\n        {\n            *(--buffer_ptr) = static_cast<char>('0' + abs_value);\n        }\n\n        o->write_characters(number_buffer.data(), n_chars);\n    }\n\n    /*!\n    @brief dump a floating-point number\n\n    Dump a given floating-point number to output stream @a o. Works internally\n    with @a number_buffer.\n\n    @param[in] x  floating-point number to dump\n    */\n    void dump_float(number_float_t x)\n    {\n        // NaN / inf\n        if (!std::isfinite(x))\n        {\n            o->write_characters(\"null\", 4);\n            return;\n        }\n\n        // If number_float_t is an IEEE-754 single or double precision number,\n        // use the Grisu2 algorithm to produce short numbers which are\n        // guaranteed to round-trip, using strtof and strtod, resp.\n        //\n        // NB: The test below works if <long double> == <double>.\n        static constexpr bool is_ieee_single_or_double\n            = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||\n              (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);\n\n        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());\n    }\n\n    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)\n    {\n        auto* begin = number_buffer.data();\n        auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);\n\n        o->write_characters(begin, static_cast<size_t>(end - begin));\n    }\n\n    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)\n    {\n        // get number of digits for a float -> text -> float round-trip\n        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;\n\n        // the actual conversion\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n        std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), \"%.*g\", d, x);\n\n        // negative value indicates an error\n        JSON_ASSERT(len > 0);\n        // check if buffer was large enough\n        JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());\n\n        // erase thousands separator\n        if (thousands_sep != '\\0')\n        {\n            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081\n            const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);\n            std::fill(end, number_buffer.end(), '\\0');\n            JSON_ASSERT((end - number_buffer.begin()) <= len);\n            len = (end - number_buffer.begin());\n        }\n\n        // convert decimal point to '.'\n        if (decimal_point != '\\0' && decimal_point != '.')\n        {\n            // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081\n            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);\n            if (dec_pos != number_buffer.end())\n            {\n                *dec_pos = '.';\n            }\n        }\n\n        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));\n\n        // determine if we need to append \".0\"\n        const bool value_is_int_like =\n            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,\n                         [](char c)\n        {\n            return c == '.' || c == 'e';\n        });\n\n        if (value_is_int_like)\n        {\n            o->write_characters(\".0\", 2);\n        }\n    }\n\n    /*!\n    @brief check whether a string is UTF-8 encoded\n\n    The function checks each byte of a string whether it is UTF-8 encoded. The\n    result of the check is stored in the @a state parameter. The function must\n    be called initially with state 0 (accept). State 1 means the string must\n    be rejected, because the current byte is not allowed. If the string is\n    completely processed, but the state is non-zero, the string ended\n    prematurely; that is, the last byte indicated more bytes should have\n    followed.\n\n    @param[in,out] state  the state of the decoding\n    @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)\n    @param[in] byte       next byte to decode\n    @return               new state\n\n    @note The function has been edited: a std::array is used.\n\n    @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>\n    @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/\n    */\n    static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept\n    {\n        static const std::array<std::uint8_t, 400> utf8d =\n        {\n            {\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F\n                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF\n                8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF\n                0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF\n                0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF\n                0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2\n                1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4\n                1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6\n                1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8\n            }\n        };\n\n        JSON_ASSERT(byte < utf8d.size());\n        const std::uint8_t type = utf8d[byte];\n\n        codep = (state != UTF8_ACCEPT)\n                ? (byte & 0x3fu) | (codep << 6u)\n                : (0xFFu >> type) & (byte);\n\n        std::size_t index = 256u + static_cast<size_t>(state) * 16u + static_cast<size_t>(type);\n        JSON_ASSERT(index < 400);\n        state = utf8d[index];\n        return state;\n    }\n\n    /*\n     * Overload to make the compiler happy while it is instantiating\n     * dump_integer for number_unsigned_t.\n     * Must never be called.\n     */\n    number_unsigned_t remove_sign(number_unsigned_t x)\n    {\n        JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        return x; // LCOV_EXCL_LINE\n    }\n\n    /*\n     * Helper function for dump_integer\n     *\n     * This function takes a negative signed integer and returns its absolute\n     * value as unsigned integer. The plus/minus shuffling is necessary as we can\n     * not directly remove the sign of an arbitrary signed integer as the\n     * absolute values of INT_MIN and INT_MAX are usually not the same. See\n     * #1708 for details.\n     */\n    inline number_unsigned_t remove_sign(number_integer_t x) noexcept\n    {\n        JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)\n        return static_cast<number_unsigned_t>(-(x + 1)) + 1;\n    }\n\n  private:\n    /// the output of the serializer\n    output_adapter_t<char> o = nullptr;\n\n    /// a (hopefully) large enough character buffer\n    std::array<char, 64> number_buffer{{}};\n\n    /// the locale\n    const std::lconv* loc = nullptr;\n    /// the locale's thousand separator character\n    const char thousands_sep = '\\0';\n    /// the locale's decimal point character\n    const char decimal_point = '\\0';\n\n    /// string buffer\n    std::array<char, 512> string_buffer{{}};\n\n    /// the indentation character\n    const char indent_char;\n    /// the indentation string\n    string_t indent_string;\n\n    /// error_handler how to react on decoding errors\n    const error_handler_t error_handler;\n};\n\n}  // namespace detail\nNLOHMANN_JSON_NAMESPACE_END\n\n// #include <nlohmann/detail/value_t.hpp>\n\n// #include <nlohmann/json_fwd.hpp>\n\n// #include <nlohmann/ordered_map.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#include <functional> // equal_to, less\n#include <initializer_list> // initializer_list\n#include <iterator> // input_iterator_tag, iterator_traits\n#include <memory> // allocator\n#include <stdexcept> // for out_of_range\n#include <type_traits> // enable_if, is_convertible\n#include <utility> // pair\n#include <vector> // vector\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/// ordered_map: a minimal map-like container that preserves insertion order\n/// for use within nlohmann::basic_json<ordered_map>\ntemplate <class Key, class T, class IgnoredLess = std::less<Key>,\n          class Allocator = std::allocator<std::pair<const Key, T>>>\n                  struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>\n{\n    using key_type = Key;\n    using mapped_type = T;\n    using Container = std::vector<std::pair<const Key, T>, Allocator>;\n    using iterator = typename Container::iterator;\n    using const_iterator = typename Container::const_iterator;\n    using size_type = typename Container::size_type;\n    using value_type = typename Container::value_type;\n#ifdef JSON_HAS_CPP_14\n    using key_compare = std::equal_to<>;\n#else\n    using key_compare = std::equal_to<Key>;\n#endif\n\n    // Explicit constructors instead of `using Container::Container`\n    // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)\n    ordered_map() noexcept(noexcept(Container())) : Container{} {}\n    explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {}\n    template <class It>\n    ordered_map(It first, It last, const Allocator& alloc = Allocator())\n        : Container{first, last, alloc} {}\n    ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )\n        : Container{init, alloc} {}\n\n    std::pair<iterator, bool> emplace(const key_type& key, T&& t)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                return {it, false};\n            }\n        }\n        Container::emplace_back(key, std::forward<T>(t));\n        return {std::prev(this->end()), true};\n    }\n\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>\n    std::pair<iterator, bool> emplace(KeyType && key, T && t)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                return {it, false};\n            }\n        }\n        Container::emplace_back(std::forward<KeyType>(key), std::forward<T>(t));\n        return {std::prev(this->end()), true};\n    }\n\n    T& operator[](const key_type& key)\n    {\n        return emplace(key, T{}).first->second;\n    }\n\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>\n    T & operator[](KeyType && key)\n    {\n        return emplace(std::forward<KeyType>(key), T{}).first->second;\n    }\n\n    const T& operator[](const key_type& key) const\n    {\n        return at(key);\n    }\n\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>\n    const T & operator[](KeyType && key) const\n    {\n        return at(std::forward<KeyType>(key));\n    }\n\n    T& at(const key_type& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                return it->second;\n            }\n        }\n\n        JSON_THROW(std::out_of_range(\"key not found\"));\n    }\n\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>\n    T & at(KeyType && key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                return it->second;\n            }\n        }\n\n        JSON_THROW(std::out_of_range(\"key not found\"));\n    }\n\n    const T& at(const key_type& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                return it->second;\n            }\n        }\n\n        JSON_THROW(std::out_of_range(\"key not found\"));\n    }\n\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>\n    const T & at(KeyType && key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                return it->second;\n            }\n        }\n\n        JSON_THROW(std::out_of_range(\"key not found\"));\n    }\n\n    size_type erase(const key_type& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                // Since we cannot move const Keys, re-construct them in place\n                for (auto next = it; ++next != this->end(); ++it)\n                {\n                    it->~value_type(); // Destroy but keep allocation\n                    new (&*it) value_type{std::move(*next)};\n                }\n                Container::pop_back();\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>\n    size_type erase(KeyType && key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                // Since we cannot move const Keys, re-construct them in place\n                for (auto next = it; ++next != this->end(); ++it)\n                {\n                    it->~value_type(); // Destroy but keep allocation\n                    new (&*it) value_type{std::move(*next)};\n                }\n                Container::pop_back();\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    iterator erase(iterator pos)\n    {\n        return erase(pos, std::next(pos));\n    }\n\n    iterator erase(iterator first, iterator last)\n    {\n        if (first == last)\n        {\n            return first;\n        }\n\n        const auto elements_affected = std::distance(first, last);\n        const auto offset = std::distance(Container::begin(), first);\n\n        // This is the start situation. We need to delete elements_affected\n        // elements (3 in this example: e, f, g), and need to return an\n        // iterator past the last deleted element (h in this example).\n        // Note that offset is the distance from the start of the vector\n        // to first. We will need this later.\n\n        // [ a, b, c, d, e, f, g, h, i, j ]\n        //               ^        ^\n        //             first    last\n\n        // Since we cannot move const Keys, we re-construct them in place.\n        // We start at first and re-construct (viz. copy) the elements from\n        // the back of the vector. Example for first iteration:\n\n        //               ,--------.\n        //               v        |   destroy e and re-construct with h\n        // [ a, b, c, d, e, f, g, h, i, j ]\n        //               ^        ^\n        //               it       it + elements_affected\n\n        for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it)\n        {\n            it->~value_type(); // destroy but keep allocation\n            new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // \"move\" next element to it\n        }\n\n        // [ a, b, c, d, h, i, j, h, i, j ]\n        //               ^        ^\n        //             first    last\n\n        // remove the unneeded elements at the end of the vector\n        Container::resize(this->size() - static_cast<size_type>(elements_affected));\n\n        // [ a, b, c, d, h, i, j ]\n        //               ^        ^\n        //             first    last\n\n        // first is now pointing past the last deleted element, but we cannot\n        // use this iterator, because it may have been invalidated by the\n        // resize call. Instead, we can return begin() + offset.\n        return Container::begin() + offset;\n    }\n\n    size_type count(const key_type& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>\n    size_type count(KeyType && key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                return 1;\n            }\n        }\n        return 0;\n    }\n\n    iterator find(const key_type& key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                return it;\n            }\n        }\n        return Container::end();\n    }\n\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>\n    iterator find(KeyType && key)\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                return it;\n            }\n        }\n        return Container::end();\n    }\n\n    const_iterator find(const key_type& key) const\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, key))\n            {\n                return it;\n            }\n        }\n        return Container::end();\n    }\n\n    std::pair<iterator, bool> insert( value_type&& value )\n    {\n        return emplace(value.first, std::move(value.second));\n    }\n\n    std::pair<iterator, bool> insert( const value_type& value )\n    {\n        for (auto it = this->begin(); it != this->end(); ++it)\n        {\n            if (m_compare(it->first, value.first))\n            {\n                return {it, false};\n            }\n        }\n        Container::push_back(value);\n        return {--this->end(), true};\n    }\n\n    template<typename InputIt>\n    using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,\n            std::input_iterator_tag>::value>::type;\n\n    template<typename InputIt, typename = require_input_iter<InputIt>>\n    void insert(InputIt first, InputIt last)\n    {\n        for (auto it = first; it != last; ++it)\n        {\n            insert(*it);\n        }\n    }\n\nprivate:\n    JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare();\n};\n\nNLOHMANN_JSON_NAMESPACE_END\n\n\n#if defined(JSON_HAS_CPP_17)\n    #include <any>\n    #include <string_view>\n#endif\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nNLOHMANN_JSON_NAMESPACE_BEGIN\n\n/*!\n@brief a class to store JSON values\n\n@internal\n@invariant The member variables @a m_value and @a m_type have the following\nrelationship:\n- If `m_type == value_t::object`, then `m_value.object != nullptr`.\n- If `m_type == value_t::array`, then `m_value.array != nullptr`.\n- If `m_type == value_t::string`, then `m_value.string != nullptr`.\nThe invariants are checked by member function assert_invariant().\n\n@note ObjectType trick from https://stackoverflow.com/a/9860911\n@endinternal\n\n@since version 1.0.0\n\n@nosubgrouping\n*/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nclass basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)\n{\n  private:\n    template<detail::value_t> friend struct detail::external_constructor;\n\n    template<typename>\n    friend class ::nlohmann::json_pointer;\n    // can be restored when json_pointer backwards compatibility is removed\n    // friend ::nlohmann::json_pointer<StringType>;\n\n    template<typename BasicJsonType, typename InputType>\n    friend class ::nlohmann::detail::parser;\n    friend ::nlohmann::detail::serializer<basic_json>;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::iter_impl;\n    template<typename BasicJsonType, typename CharType>\n    friend class ::nlohmann::detail::binary_writer;\n    template<typename BasicJsonType, typename InputType, typename SAX>\n    friend class ::nlohmann::detail::binary_reader;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_parser;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_callback_parser;\n    friend class ::nlohmann::detail::exception;\n\n    /// workaround type for MSVC\n    using basic_json_t = NLOHMANN_BASIC_JSON_TPL;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    // convenience aliases for types residing in namespace detail;\n    using lexer = ::nlohmann::detail::lexer_base<basic_json>;\n\n    template<typename InputAdapterType>\n    static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser(\n        InputAdapterType adapter,\n        detail::parser_callback_t<basic_json>cb = nullptr,\n        const bool allow_exceptions = true,\n        const bool ignore_comments = false\n                                 )\n    {\n        return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),\n                std::move(cb), allow_exceptions, ignore_comments);\n    }\n\n  private:\n    using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;\n    template<typename BasicJsonType>\n    using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;\n    template<typename BasicJsonType>\n    using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;\n    template<typename Iterator>\n    using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;\n    template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;\n\n    template<typename CharType>\n    using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;\n\n    template<typename InputType>\n    using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;\n    template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    using serializer = ::nlohmann::detail::serializer<basic_json>;\n\n  public:\n    using value_t = detail::value_t;\n    /// JSON Pointer, see @ref nlohmann::json_pointer\n    using json_pointer = ::nlohmann::json_pointer<StringType>;\n    template<typename T, typename SFINAE>\n    using json_serializer = JSONSerializer<T, SFINAE>;\n    /// how to treat decoding errors\n    using error_handler_t = detail::error_handler_t;\n    /// how to treat CBOR tags\n    using cbor_tag_handler_t = detail::cbor_tag_handler_t;\n    /// helper type for initializer lists of basic_json values\n    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;\n\n    using input_format_t = detail::input_format_t;\n    /// SAX interface type, see @ref nlohmann::json_sax\n    using json_sax_t = json_sax<basic_json>;\n\n    ////////////////\n    // exceptions //\n    ////////////////\n\n    /// @name exceptions\n    /// Classes to implement user-defined exceptions.\n    /// @{\n\n    using exception = detail::exception;\n    using parse_error = detail::parse_error;\n    using invalid_iterator = detail::invalid_iterator;\n    using type_error = detail::type_error;\n    using out_of_range = detail::out_of_range;\n    using other_error = detail::other_error;\n\n    /// @}\n\n\n    /////////////////////\n    // container types //\n    /////////////////////\n\n    /// @name container types\n    /// The canonic container types to use @ref basic_json like any other STL\n    /// container.\n    /// @{\n\n    /// the type of elements in a basic_json container\n    using value_type = basic_json;\n\n    /// the type of an element reference\n    using reference = value_type&;\n    /// the type of an element const reference\n    using const_reference = const value_type&;\n\n    /// a type to represent differences between iterators\n    using difference_type = std::ptrdiff_t;\n    /// a type to represent container sizes\n    using size_type = std::size_t;\n\n    /// the allocator type\n    using allocator_type = AllocatorType<basic_json>;\n\n    /// the type of an element pointer\n    using pointer = typename std::allocator_traits<allocator_type>::pointer;\n    /// the type of an element const pointer\n    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;\n\n    /// an iterator for a basic_json container\n    using iterator = iter_impl<basic_json>;\n    /// a const iterator for a basic_json container\n    using const_iterator = iter_impl<const basic_json>;\n    /// a reverse iterator for a basic_json container\n    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;\n    /// a const reverse iterator for a basic_json container\n    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;\n\n    /// @}\n\n\n    /// @brief returns the allocator associated with the container\n    /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/\n    static allocator_type get_allocator()\n    {\n        return allocator_type();\n    }\n\n    /// @brief returns version information on the library\n    /// @sa https://json.nlohmann.me/api/basic_json/meta/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json meta()\n    {\n        basic_json result;\n\n        result[\"copyright\"] = \"(C) 2013-2022 Niels Lohmann\";\n        result[\"name\"] = \"JSON for Modern C++\";\n        result[\"url\"] = \"https://github.com/nlohmann/json\";\n        result[\"version\"][\"string\"] =\n            detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.',\n                           std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.',\n                           std::to_string(NLOHMANN_JSON_VERSION_PATCH));\n        result[\"version\"][\"major\"] = NLOHMANN_JSON_VERSION_MAJOR;\n        result[\"version\"][\"minor\"] = NLOHMANN_JSON_VERSION_MINOR;\n        result[\"version\"][\"patch\"] = NLOHMANN_JSON_VERSION_PATCH;\n\n#ifdef _WIN32\n        result[\"platform\"] = \"win32\";\n#elif defined __linux__\n        result[\"platform\"] = \"linux\";\n#elif defined __APPLE__\n        result[\"platform\"] = \"apple\";\n#elif defined __unix__\n        result[\"platform\"] = \"unix\";\n#else\n        result[\"platform\"] = \"unknown\";\n#endif\n\n#if defined(__ICC) || defined(__INTEL_COMPILER)\n        result[\"compiler\"] = {{\"family\", \"icc\"}, {\"version\", __INTEL_COMPILER}};\n#elif defined(__clang__)\n        result[\"compiler\"] = {{\"family\", \"clang\"}, {\"version\", __clang_version__}};\n#elif defined(__GNUC__) || defined(__GNUG__)\n        result[\"compiler\"] = {{\"family\", \"gcc\"}, {\"version\", detail::concat(\n                    std::to_string(__GNUC__), '.',\n                    std::to_string(__GNUC_MINOR__), '.',\n                    std::to_string(__GNUC_PATCHLEVEL__))\n            }\n        };\n#elif defined(__HP_cc) || defined(__HP_aCC)\n        result[\"compiler\"] = \"hp\"\n#elif defined(__IBMCPP__)\n        result[\"compiler\"] = {{\"family\", \"ilecpp\"}, {\"version\", __IBMCPP__}};\n#elif defined(_MSC_VER)\n        result[\"compiler\"] = {{\"family\", \"msvc\"}, {\"version\", _MSC_VER}};\n#elif defined(__PGI)\n        result[\"compiler\"] = {{\"family\", \"pgcpp\"}, {\"version\", __PGI}};\n#elif defined(__SUNPRO_CC)\n        result[\"compiler\"] = {{\"family\", \"sunpro\"}, {\"version\", __SUNPRO_CC}};\n#else\n        result[\"compiler\"] = {{\"family\", \"unknown\"}, {\"version\", \"unknown\"}};\n#endif\n\n\n#if defined(_MSVC_LANG)\n        result[\"compiler\"][\"c++\"] = std::to_string(_MSVC_LANG);\n#elif defined(__cplusplus)\n        result[\"compiler\"][\"c++\"] = std::to_string(__cplusplus);\n#else\n        result[\"compiler\"][\"c++\"] = \"unknown\";\n#endif\n        return result;\n    }\n\n\n    ///////////////////////////\n    // JSON value data types //\n    ///////////////////////////\n\n    /// @name JSON value data types\n    /// The data types to store a JSON value. These types are derived from\n    /// the template arguments passed to class @ref basic_json.\n    /// @{\n\n    /// @brief default object key comparator type\n    /// The actual object key comparator type (@ref object_comparator_t) may be\n    /// different.\n    /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/\n#if defined(JSON_HAS_CPP_14)\n    // use of transparent comparator avoids unnecessary repeated construction of temporaries\n    // in functions involving lookup by key with types other than object_t::key_type (aka. StringType)\n    using default_object_comparator_t = std::less<>;\n#else\n    using default_object_comparator_t = std::less<StringType>;\n#endif\n\n    /// @brief a type for an object\n    /// @sa https://json.nlohmann.me/api/basic_json/object_t/\n    using object_t = ObjectType<StringType,\n          basic_json,\n          default_object_comparator_t,\n          AllocatorType<std::pair<const StringType,\n          basic_json>>>;\n\n    /// @brief a type for an array\n    /// @sa https://json.nlohmann.me/api/basic_json/array_t/\n    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;\n\n    /// @brief a type for a string\n    /// @sa https://json.nlohmann.me/api/basic_json/string_t/\n    using string_t = StringType;\n\n    /// @brief a type for a boolean\n    /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/\n    using boolean_t = BooleanType;\n\n    /// @brief a type for a number (integer)\n    /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/\n    using number_integer_t = NumberIntegerType;\n\n    /// @brief a type for a number (unsigned)\n    /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/\n    using number_unsigned_t = NumberUnsignedType;\n\n    /// @brief a type for a number (floating-point)\n    /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/\n    using number_float_t = NumberFloatType;\n\n    /// @brief a type for a packed binary type\n    /// @sa https://json.nlohmann.me/api/basic_json/binary_t/\n    using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;\n\n    /// @brief object key comparator type\n    /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/\n    using object_comparator_t = detail::actual_object_comparator_t<basic_json>;\n\n    /// @}\n\n  private:\n\n    /// helper for exception-safe object creation\n    template<typename T, typename... Args>\n    JSON_HEDLEY_RETURNS_NON_NULL\n    static T* create(Args&& ... args)\n    {\n        AllocatorType<T> alloc;\n        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;\n\n        auto deleter = [&](T * obj)\n        {\n            AllocatorTraits::deallocate(alloc, obj, 1);\n        };\n        std::unique_ptr<T, decltype(deleter)> obj(AllocatorTraits::allocate(alloc, 1), deleter);\n        AllocatorTraits::construct(alloc, obj.get(), std::forward<Args>(args)...);\n        JSON_ASSERT(obj != nullptr);\n        return obj.release();\n    }\n\n    ////////////////////////\n    // JSON value storage //\n    ////////////////////////\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    /*!\n    @brief a JSON value\n\n    The actual storage for a JSON value of the @ref basic_json class. This\n    union combines the different storage types for the JSON value types\n    defined in @ref value_t.\n\n    JSON type | value_t type    | used type\n    --------- | --------------- | ------------------------\n    object    | object          | pointer to @ref object_t\n    array     | array           | pointer to @ref array_t\n    string    | string          | pointer to @ref string_t\n    boolean   | boolean         | @ref boolean_t\n    number    | number_integer  | @ref number_integer_t\n    number    | number_unsigned | @ref number_unsigned_t\n    number    | number_float    | @ref number_float_t\n    binary    | binary          | pointer to @ref binary_t\n    null      | null            | *no value is stored*\n\n    @note Variable-length types (objects, arrays, and strings) are stored as\n    pointers. The size of the union should not exceed 64 bits if the default\n    value types are used.\n\n    @since version 1.0.0\n    */\n    union json_value\n    {\n        /// object (stored with pointer to save storage)\n        object_t* object;\n        /// array (stored with pointer to save storage)\n        array_t* array;\n        /// string (stored with pointer to save storage)\n        string_t* string;\n        /// binary (stored with pointer to save storage)\n        binary_t* binary;\n        /// boolean\n        boolean_t boolean;\n        /// number (integer)\n        number_integer_t number_integer;\n        /// number (unsigned integer)\n        number_unsigned_t number_unsigned;\n        /// number (floating-point)\n        number_float_t number_float;\n\n        /// default constructor (for null values)\n        json_value() = default;\n        /// constructor for booleans\n        json_value(boolean_t v) noexcept : boolean(v) {}\n        /// constructor for numbers (integer)\n        json_value(number_integer_t v) noexcept : number_integer(v) {}\n        /// constructor for numbers (unsigned)\n        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}\n        /// constructor for numbers (floating-point)\n        json_value(number_float_t v) noexcept : number_float(v) {}\n        /// constructor for empty values of a given type\n        json_value(value_t t)\n        {\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    object = create<object_t>();\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    array = create<array_t>();\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    string = create<string_t>(\"\");\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    binary = create<binary_t>();\n                    break;\n                }\n\n                case value_t::boolean:\n                {\n                    boolean = static_cast<boolean_t>(false);\n                    break;\n                }\n\n                case value_t::number_integer:\n                {\n                    number_integer = static_cast<number_integer_t>(0);\n                    break;\n                }\n\n                case value_t::number_unsigned:\n                {\n                    number_unsigned = static_cast<number_unsigned_t>(0);\n                    break;\n                }\n\n                case value_t::number_float:\n                {\n                    number_float = static_cast<number_float_t>(0.0);\n                    break;\n                }\n\n                case value_t::null:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    break;\n                }\n\n                case value_t::discarded:\n                default:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    if (JSON_HEDLEY_UNLIKELY(t == value_t::null))\n                    {\n                        JSON_THROW(other_error::create(500, \"961c151d2e87f2686a955a9be24d316f1362bf21 3.11.2\", nullptr)); // LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n            }\n        }\n\n        /// constructor for strings\n        json_value(const string_t& value) : string(create<string_t>(value)) {}\n\n        /// constructor for rvalue strings\n        json_value(string_t&& value) : string(create<string_t>(std::move(value))) {}\n\n        /// constructor for objects\n        json_value(const object_t& value) : object(create<object_t>(value)) {}\n\n        /// constructor for rvalue objects\n        json_value(object_t&& value) : object(create<object_t>(std::move(value))) {}\n\n        /// constructor for arrays\n        json_value(const array_t& value) : array(create<array_t>(value)) {}\n\n        /// constructor for rvalue arrays\n        json_value(array_t&& value) : array(create<array_t>(std::move(value))) {}\n\n        /// constructor for binary arrays\n        json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {}\n\n        /// constructor for rvalue binary arrays\n        json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {}\n\n        /// constructor for binary arrays (internal type)\n        json_value(const binary_t& value) : binary(create<binary_t>(value)) {}\n\n        /// constructor for rvalue binary arrays (internal type)\n        json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}\n\n        void destroy(value_t t)\n        {\n            if (t == value_t::array || t == value_t::object)\n            {\n                // flatten the current json_value to a heap-allocated stack\n                std::vector<basic_json> stack;\n\n                // move the top-level items to stack\n                if (t == value_t::array)\n                {\n                    stack.reserve(array->size());\n                    std::move(array->begin(), array->end(), std::back_inserter(stack));\n                }\n                else\n                {\n                    stack.reserve(object->size());\n                    for (auto&& it : *object)\n                    {\n                        stack.push_back(std::move(it.second));\n                    }\n                }\n\n                while (!stack.empty())\n                {\n                    // move the last item to local variable to be processed\n                    basic_json current_item(std::move(stack.back()));\n                    stack.pop_back();\n\n                    // if current_item is array/object, move\n                    // its children to the stack to be processed later\n                    if (current_item.is_array())\n                    {\n                        std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack));\n\n                        current_item.m_value.array->clear();\n                    }\n                    else if (current_item.is_object())\n                    {\n                        for (auto&& it : *current_item.m_value.object)\n                        {\n                            stack.push_back(std::move(it.second));\n                        }\n\n                        current_item.m_value.object->clear();\n                    }\n\n                    // it's now safe that current_item get destructed\n                    // since it doesn't have any children\n                }\n            }\n\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    AllocatorType<object_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, object);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    AllocatorType<array_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, array);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);\n                    break;\n                }\n\n                case value_t::binary:\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);\n                    break;\n                }\n\n                case value_t::null:\n                case value_t::boolean:\n                case value_t::number_integer:\n                case value_t::number_unsigned:\n                case value_t::number_float:\n                case value_t::discarded:\n                default:\n                {\n                    break;\n                }\n            }\n        }\n    };\n\n  private:\n    /*!\n    @brief checks the class invariants\n\n    This function asserts the class invariants. It needs to be called at the\n    end of every constructor to make sure that created objects respect the\n    invariant. Furthermore, it has to be called each time the type of a JSON\n    value is changed, because the invariant expresses a relationship between\n    @a m_type and @a m_value.\n\n    Furthermore, the parent relation is checked for arrays and objects: If\n    @a check_parents true and the value is an array or object, then the\n    container's elements must have the current value as parent.\n\n    @param[in] check_parents  whether the parent relation should be checked.\n               The value is true by default and should only be set to false\n               during destruction of objects when the invariant does not\n               need to hold.\n    */\n    void assert_invariant(bool check_parents = true) const noexcept\n    {\n        JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr);\n        JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr);\n        JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr);\n        JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr);\n\n#if JSON_DIAGNOSTICS\n        JSON_TRY\n        {\n            // cppcheck-suppress assertWithSideEffect\n            JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)\n            {\n                return j.m_parent == this;\n            }));\n        }\n        JSON_CATCH(...) {} // LCOV_EXCL_LINE\n#endif\n        static_cast<void>(check_parents);\n    }\n\n    void set_parents()\n    {\n#if JSON_DIAGNOSTICS\n        switch (m_type)\n        {\n            case value_t::array:\n            {\n                for (auto& element : *m_value.array)\n                {\n                    element.m_parent = this;\n                }\n                break;\n            }\n\n            case value_t::object:\n            {\n                for (auto& element : *m_value.object)\n                {\n                    element.second.m_parent = this;\n                }\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                break;\n        }\n#endif\n    }\n\n    iterator set_parents(iterator it, typename iterator::difference_type count_set_parents)\n    {\n#if JSON_DIAGNOSTICS\n        for (typename iterator::difference_type i = 0; i < count_set_parents; ++i)\n        {\n            (it + i)->m_parent = this;\n        }\n#else\n        static_cast<void>(count_set_parents);\n#endif\n        return it;\n    }\n\n    reference set_parent(reference j, std::size_t old_capacity = static_cast<std::size_t>(-1))\n    {\n#if JSON_DIAGNOSTICS\n        if (old_capacity != static_cast<std::size_t>(-1))\n        {\n            // see https://github.com/nlohmann/json/issues/2838\n            JSON_ASSERT(type() == value_t::array);\n            if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))\n            {\n                // capacity has changed: update all parents\n                set_parents();\n                return j;\n            }\n        }\n\n        // ordered_json uses a vector internally, so pointers could have\n        // been invalidated; see https://github.com/nlohmann/json/issues/2962\n#ifdef JSON_HEDLEY_MSVC_VERSION\n#pragma warning(push )\n#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr\n#endif\n        if (detail::is_ordered_map<object_t>::value)\n        {\n            set_parents();\n            return j;\n        }\n#ifdef JSON_HEDLEY_MSVC_VERSION\n#pragma warning( pop )\n#endif\n\n        j.m_parent = this;\n#else\n        static_cast<void>(j);\n        static_cast<void>(old_capacity);\n#endif\n        return j;\n    }\n\n  public:\n    //////////////////////////\n    // JSON parser callback //\n    //////////////////////////\n\n    /// @brief parser event types\n    /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/\n    using parse_event_t = detail::parse_event_t;\n\n    /// @brief per-element parser callback type\n    /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/\n    using parser_callback_t = detail::parser_callback_t<basic_json>;\n\n    //////////////////\n    // constructors //\n    //////////////////\n\n    /// @name constructors and destructors\n    /// Constructors of class @ref basic_json, copy/move constructor, copy\n    /// assignment, static functions creating objects, and the destructor.\n    /// @{\n\n    /// @brief create an empty value with a given type\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(const value_t v)\n        : m_type(v), m_value(v)\n    {\n        assert_invariant();\n    }\n\n    /// @brief create a null object\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape)\n        : basic_json(value_t::null)\n    {\n        assert_invariant();\n    }\n\n    /// @brief create a JSON value from compatible types\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    template < typename CompatibleType,\n               typename U = detail::uncvref_t<CompatibleType>,\n               detail::enable_if_t <\n                   !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >\n    basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)\n                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),\n                                           std::forward<CompatibleType>(val))))\n    {\n        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief create a JSON value from an existing one\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    template < typename BasicJsonType,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >\n    basic_json(const BasicJsonType& val)\n    {\n        using other_boolean_t = typename BasicJsonType::boolean_t;\n        using other_number_float_t = typename BasicJsonType::number_float_t;\n        using other_number_integer_t = typename BasicJsonType::number_integer_t;\n        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n        using other_string_t = typename BasicJsonType::string_t;\n        using other_object_t = typename BasicJsonType::object_t;\n        using other_array_t = typename BasicJsonType::array_t;\n        using other_binary_t = typename BasicJsonType::binary_t;\n\n        switch (val.type())\n        {\n            case value_t::boolean:\n                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());\n                break;\n            case value_t::number_float:\n                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());\n                break;\n            case value_t::number_integer:\n                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());\n                break;\n            case value_t::number_unsigned:\n                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());\n                break;\n            case value_t::string:\n                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());\n                break;\n            case value_t::object:\n                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());\n                break;\n            case value_t::array:\n                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());\n                break;\n            case value_t::binary:\n                JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());\n                break;\n            case value_t::null:\n                *this = nullptr;\n                break;\n            case value_t::discarded:\n                m_type = value_t::discarded;\n                break;\n            default:            // LCOV_EXCL_LINE\n                JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n        }\n        JSON_ASSERT(m_type == val.type());\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief create a container (array or object) from an initializer list\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(initializer_list_t init,\n               bool type_deduction = true,\n               value_t manual_type = value_t::array)\n    {\n        // check if each element is an array with two elements whose first\n        // element is a string\n        bool is_an_object = std::all_of(init.begin(), init.end(),\n                                        [](const detail::json_ref<basic_json>& element_ref)\n        {\n            return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string();\n        });\n\n        // adjust type if type deduction is not wanted\n        if (!type_deduction)\n        {\n            // if array is wanted, do not create an object though possible\n            if (manual_type == value_t::array)\n            {\n                is_an_object = false;\n            }\n\n            // if object is wanted but impossible, throw an exception\n            if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))\n            {\n                JSON_THROW(type_error::create(301, \"cannot create object from initializer list\", nullptr));\n            }\n        }\n\n        if (is_an_object)\n        {\n            // the initializer list is a list of pairs -> create object\n            m_type = value_t::object;\n            m_value = value_t::object;\n\n            for (auto& element_ref : init)\n            {\n                auto element = element_ref.moved_or_copied();\n                m_value.object->emplace(\n                    std::move(*((*element.m_value.array)[0].m_value.string)),\n                    std::move((*element.m_value.array)[1]));\n            }\n        }\n        else\n        {\n            // the initializer list describes an array -> create array\n            m_type = value_t::array;\n            m_value.array = create<array_t>(init.begin(), init.end());\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief explicitly create a binary array (without subtype)\n    /// @sa https://json.nlohmann.me/api/basic_json/binary/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = init;\n        return res;\n    }\n\n    /// @brief explicitly create a binary array (with subtype)\n    /// @sa https://json.nlohmann.me/api/basic_json/binary/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = binary_t(init, subtype);\n        return res;\n    }\n\n    /// @brief explicitly create a binary array\n    /// @sa https://json.nlohmann.me/api/basic_json/binary/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = std::move(init);\n        return res;\n    }\n\n    /// @brief explicitly create a binary array (with subtype)\n    /// @sa https://json.nlohmann.me/api/basic_json/binary/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)\n    {\n        auto res = basic_json();\n        res.m_type = value_t::binary;\n        res.m_value = binary_t(std::move(init), subtype);\n        return res;\n    }\n\n    /// @brief explicitly create an array from an initializer list\n    /// @sa https://json.nlohmann.me/api/basic_json/array/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json array(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::array);\n    }\n\n    /// @brief explicitly create an object from an initializer list\n    /// @sa https://json.nlohmann.me/api/basic_json/object/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json object(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::object);\n    }\n\n    /// @brief construct an array with count copies of given value\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(size_type cnt, const basic_json& val)\n        : m_type(value_t::array)\n    {\n        m_value.array = create<array_t>(cnt, val);\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief construct a JSON container given an iterator range\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    template < class InputIT, typename std::enable_if <\n                   std::is_same<InputIT, typename basic_json_t::iterator>::value ||\n                   std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >\n    basic_json(InputIT first, InputIT last)\n    {\n        JSON_ASSERT(first.m_object != nullptr);\n        JSON_ASSERT(last.m_object != nullptr);\n\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(201, \"iterators are not compatible\", nullptr));\n        }\n\n        // copy type from first iterator\n        m_type = first.m_object->m_type;\n\n        // check if iterator range is complete for primitive values\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()\n                                         || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\", first.m_object));\n                }\n                break;\n            }\n\n            case value_t::null:\n            case value_t::object:\n            case value_t::array:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n                break;\n        }\n\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = first.m_object->m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = first.m_object->m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = first.m_object->m_value.number_float;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = first.m_object->m_value.boolean;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *first.m_object->m_value.string;\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object = create<object_t>(first.m_it.object_iterator,\n                                                  last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array = create<array_t>(first.m_it.array_iterator,\n                                                last.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value = *first.m_object->m_value.binary;\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(invalid_iterator::create(206, detail::concat(\"cannot construct with iterators from \", first.m_object->type_name()), first.m_object));\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n\n    ///////////////////////////////////////\n    // other constructors and destructor //\n    ///////////////////////////////////////\n\n    template<typename JsonRef,\n             detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,\n                                 std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >\n    basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}\n\n    /// @brief copy constructor\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(const basic_json& other)\n        : m_type(other.m_type)\n    {\n        // check of passed value is valid\n        other.assert_invariant();\n\n        switch (m_type)\n        {\n            case value_t::object:\n            {\n                m_value = *other.m_value.object;\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value = *other.m_value.array;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *other.m_value.string;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value = other.m_value.boolean;\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                m_value = other.m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value = other.m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value = other.m_value.number_float;\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value = *other.m_value.binary;\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                break;\n        }\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief move constructor\n    /// @sa https://json.nlohmann.me/api/basic_json/basic_json/\n    basic_json(basic_json&& other) noexcept\n        : m_type(std::move(other.m_type)),\n          m_value(std::move(other.m_value))\n    {\n        // check that passed value is valid\n        other.assert_invariant(false);\n\n        // invalidate payload\n        other.m_type = value_t::null;\n        other.m_value = {};\n\n        set_parents();\n        assert_invariant();\n    }\n\n    /// @brief copy assignment\n    /// @sa https://json.nlohmann.me/api/basic_json/operator=/\n    basic_json& operator=(basic_json other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        // check that passed value is valid\n        other.assert_invariant();\n\n        using std::swap;\n        swap(m_type, other.m_type);\n        swap(m_value, other.m_value);\n\n        set_parents();\n        assert_invariant();\n        return *this;\n    }\n\n    /// @brief destructor\n    /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/\n    ~basic_json() noexcept\n    {\n        assert_invariant(false);\n        m_value.destroy(m_type);\n    }\n\n    /// @}\n\n  public:\n    ///////////////////////\n    // object inspection //\n    ///////////////////////\n\n    /// @name object inspection\n    /// Functions to inspect the type of a JSON value.\n    /// @{\n\n    /// @brief serialization\n    /// @sa https://json.nlohmann.me/api/basic_json/dump/\n    string_t dump(const int indent = -1,\n                  const char indent_char = ' ',\n                  const bool ensure_ascii = false,\n                  const error_handler_t error_handler = error_handler_t::strict) const\n    {\n        string_t result;\n        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);\n\n        if (indent >= 0)\n        {\n            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));\n        }\n        else\n        {\n            s.dump(*this, false, ensure_ascii, 0);\n        }\n\n        return result;\n    }\n\n    /// @brief return the type of the JSON value (explicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/type/\n    constexpr value_t type() const noexcept\n    {\n        return m_type;\n    }\n\n    /// @brief return whether type is primitive\n    /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/\n    constexpr bool is_primitive() const noexcept\n    {\n        return is_null() || is_string() || is_boolean() || is_number() || is_binary();\n    }\n\n    /// @brief return whether type is structured\n    /// @sa https://json.nlohmann.me/api/basic_json/is_structured/\n    constexpr bool is_structured() const noexcept\n    {\n        return is_array() || is_object();\n    }\n\n    /// @brief return whether value is null\n    /// @sa https://json.nlohmann.me/api/basic_json/is_null/\n    constexpr bool is_null() const noexcept\n    {\n        return m_type == value_t::null;\n    }\n\n    /// @brief return whether value is a boolean\n    /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/\n    constexpr bool is_boolean() const noexcept\n    {\n        return m_type == value_t::boolean;\n    }\n\n    /// @brief return whether value is a number\n    /// @sa https://json.nlohmann.me/api/basic_json/is_number/\n    constexpr bool is_number() const noexcept\n    {\n        return is_number_integer() || is_number_float();\n    }\n\n    /// @brief return whether value is an integer number\n    /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/\n    constexpr bool is_number_integer() const noexcept\n    {\n        return m_type == value_t::number_integer || m_type == value_t::number_unsigned;\n    }\n\n    /// @brief return whether value is an unsigned integer number\n    /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/\n    constexpr bool is_number_unsigned() const noexcept\n    {\n        return m_type == value_t::number_unsigned;\n    }\n\n    /// @brief return whether value is a floating-point number\n    /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/\n    constexpr bool is_number_float() const noexcept\n    {\n        return m_type == value_t::number_float;\n    }\n\n    /// @brief return whether value is an object\n    /// @sa https://json.nlohmann.me/api/basic_json/is_object/\n    constexpr bool is_object() const noexcept\n    {\n        return m_type == value_t::object;\n    }\n\n    /// @brief return whether value is an array\n    /// @sa https://json.nlohmann.me/api/basic_json/is_array/\n    constexpr bool is_array() const noexcept\n    {\n        return m_type == value_t::array;\n    }\n\n    /// @brief return whether value is a string\n    /// @sa https://json.nlohmann.me/api/basic_json/is_string/\n    constexpr bool is_string() const noexcept\n    {\n        return m_type == value_t::string;\n    }\n\n    /// @brief return whether value is a binary array\n    /// @sa https://json.nlohmann.me/api/basic_json/is_binary/\n    constexpr bool is_binary() const noexcept\n    {\n        return m_type == value_t::binary;\n    }\n\n    /// @brief return whether value is discarded\n    /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/\n    constexpr bool is_discarded() const noexcept\n    {\n        return m_type == value_t::discarded;\n    }\n\n    /// @brief return the type of the JSON value (implicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/\n    constexpr operator value_t() const noexcept\n    {\n        return m_type;\n    }\n\n    /// @}\n\n  private:\n    //////////////////\n    // value access //\n    //////////////////\n\n    /// get a boolean (explicit)\n    boolean_t get_impl(boolean_t* /*unused*/) const\n    {\n        if (JSON_HEDLEY_LIKELY(is_boolean()))\n        {\n            return m_value.boolean;\n        }\n\n        JSON_THROW(type_error::create(302, detail::concat(\"type must be boolean, but is \", type_name()), this));\n    }\n\n    /// get a pointer to the value (object)\n    object_t* get_impl_ptr(object_t* /*unused*/) noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (object)\n    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    array_t* get_impl_ptr(array_t* /*unused*/) noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    string_t* get_impl_ptr(string_t* /*unused*/) noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept\n    {\n        return is_binary() ? m_value.binary : nullptr;\n    }\n\n    /// get a pointer to the value (binary)\n    constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept\n    {\n        return is_binary() ? m_value.binary : nullptr;\n    }\n\n    /*!\n    @brief helper function to implement get_ref()\n\n    This function helps to implement get_ref() without code duplication for\n    const and non-const overloads\n\n    @tparam ThisType will be deduced as `basic_json` or `const basic_json`\n\n    @throw type_error.303 if ReferenceType does not match underlying value\n    type of the current JSON\n    */\n    template<typename ReferenceType, typename ThisType>\n    static ReferenceType get_ref_impl(ThisType& obj)\n    {\n        // delegate the call to get_ptr<>()\n        auto* ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();\n\n        if (JSON_HEDLEY_LIKELY(ptr != nullptr))\n        {\n            return *ptr;\n        }\n\n        JSON_THROW(type_error::create(303, detail::concat(\"incompatible ReferenceType for get_ref, actual type is \", obj.type_name()), &obj));\n    }\n\n  public:\n    /// @name value access\n    /// Direct access to the stored value of a JSON value.\n    /// @{\n\n    /// @brief get a pointer value (implicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>()\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /// @brief get a pointer value (implicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/\n    template < typename PointerType, typename std::enable_if <\n                   std::is_pointer<PointerType>::value&&\n                   std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >\n    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>() const\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n  private:\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType ret;\n    JSONSerializer<ValueType>::from_json(*this, ret);\n    return ret;\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n    - @ref json_serializer<ValueType> does not have a `from_json()` method of\n      the form `ValueType from_json(const basic_json&)`\n\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get__ValueType_const}\n\n    @since version 2.1.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   detail::is_default_constructible<ValueType>::value&&\n                   detail::has_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))\n    {\n        auto ret = ValueType();\n        JSONSerializer<ValueType>::from_json(*this, ret);\n        return ret;\n    }\n\n    /*!\n    @brief get a value (explicit); special case\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    return JSONSerializer<ValueType>::from_json(*this);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json and\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `ValueType from_json(const basic_json&)`\n\n    @note If @ref json_serializer<ValueType> has both overloads of\n    `from_json()`, this one is chosen.\n\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @since version 2.1.0\n    */\n    template < typename ValueType,\n               detail::enable_if_t <\n                   detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))\n    {\n        return JSONSerializer<ValueType>::from_json(*this);\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads converts the current @ref basic_json in a different\n    @ref basic_json type\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this, converted into @a BasicJsonType\n\n    @complexity Depending on the implementation of the called `from_json()`\n                method.\n\n    @since version 3.2.0\n    */\n    template < typename BasicJsonType,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value,\n                   int > = 0 >\n    BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads avoids a lot of template boilerplate, it can be seen as the\n    identity method\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this\n\n    @complexity Constant.\n\n    @since version 2.1.0\n    */\n    template<typename BasicJsonType,\n             detail::enable_if_t<\n                 std::is_same<BasicJsonType, basic_json_t>::value,\n                 int> = 0>\n    basic_json get_impl(detail::priority_tag<3> /*unused*/) const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n    @copydoc get()\n    */\n    template<typename PointerType,\n             detail::enable_if_t<\n                 std::is_pointer<PointerType>::value,\n                 int> = 0>\n    constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept\n    -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n  public:\n    /*!\n    @brief get a (pointer) value (explicit)\n\n    Performs explicit type conversion between the JSON value and a compatible value if required.\n\n    - If the requested type is a pointer to the internally stored JSON value that pointer is returned.\n    No copies are made.\n\n    - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible\n    from the current @ref basic_json.\n\n    - Otherwise the value is converted by calling the @ref json_serializer<ValueType> `from_json()`\n    method.\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @tparam ValueType if necessary\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required\n\n    @since version 2.1.0\n    */\n    template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>\n#if defined(JSON_HAS_CPP_14)\n    constexpr\n#endif\n    auto get() const noexcept(\n    noexcept(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {})))\n    -> decltype(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {}))\n    {\n        // we cannot static_assert on ValueTypeCV being non-const, because\n        // there is support for get<const basic_json_t>(), which is why we\n        // still need the uncvref\n        static_assert(!std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        return get_impl<ValueType>(detail::priority_tag<4> {});\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n\n    Explicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning The pointer becomes invalid if the underlying JSON object\n    changes.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get__PointerType}\n\n    @sa see @ref get_ptr() for explicit pointer-member access\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /// @brief get a value (explicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/get_to/\n    template < typename ValueType,\n               detail::enable_if_t <\n                   !detail::is_basic_json<ValueType>::value&&\n                   detail::has_from_json<basic_json_t, ValueType>::value,\n                   int > = 0 >\n    ValueType & get_to(ValueType& v) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<ValueType>::from_json(*this, v);\n        return v;\n    }\n\n    // specialization to allow calling get_to with a basic_json value\n    // see https://github.com/nlohmann/json/issues/2175\n    template<typename ValueType,\n             detail::enable_if_t <\n                 detail::is_basic_json<ValueType>::value,\n                 int> = 0>\n    ValueType & get_to(ValueType& v) const\n    {\n        v = *this;\n        return v;\n    }\n\n    template <\n        typename T, std::size_t N,\n        typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n        detail::enable_if_t <\n            detail::has_from_json<basic_json_t, Array>::value, int > = 0 >\n    Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)\n    noexcept(noexcept(JSONSerializer<Array>::from_json(\n                          std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<Array>::from_json(*this, v);\n        return v;\n    }\n\n    /// @brief get a reference value (implicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/get_ref/\n    template<typename ReferenceType, typename std::enable_if<\n                 std::is_reference<ReferenceType>::value, int>::type = 0>\n    ReferenceType get_ref()\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /// @brief get a reference value (implicit)\n    /// @sa https://json.nlohmann.me/api/basic_json/get_ref/\n    template < typename ReferenceType, typename std::enable_if <\n                   std::is_reference<ReferenceType>::value&&\n                   std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >\n    ReferenceType get_ref() const\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a value (implicit)\n\n    Implicit type conversion between the JSON value and a compatible value.\n    The call is realized by calling @ref get() const.\n\n    @tparam ValueType non-pointer type compatible to the JSON value, for\n    instance `int` for JSON integer numbers, `bool` for JSON booleans, or\n    `std::vector` types for JSON arrays. The character type of @ref string_t\n    as well as an initializer list of this type is excluded to avoid\n    ambiguities as these types implicitly convert to `std::string`.\n\n    @return copy of the JSON value, converted to type @a ValueType\n\n    @throw type_error.302 in case passed type @a ValueType is incompatible\n    to the JSON value type (e.g., the JSON value is of type boolean, but a\n    string is requested); see example below\n\n    @complexity Linear in the size of the JSON value.\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,operator__ValueType}\n\n    @since version 1.0.0\n    */\n    template < typename ValueType, typename std::enable_if <\n                   detail::conjunction <\n                       detail::negation<std::is_pointer<ValueType>>,\n                       detail::negation<std::is_same<ValueType, std::nullptr_t>>,\n                       detail::negation<std::is_same<ValueType, detail::json_ref<basic_json>>>,\n                                        detail::negation<std::is_same<ValueType, typename string_t::value_type>>,\n                                        detail::negation<detail::is_basic_json<ValueType>>,\n                                        detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,\n#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))\n                                                detail::negation<std::is_same<ValueType, std::string_view>>,\n#endif\n#if defined(JSON_HAS_CPP_17)\n                                                detail::negation<std::is_same<ValueType, std::any>>,\n#endif\n                                                detail::is_detected_lazy<detail::get_template_function, const basic_json_t&, ValueType>\n                                                >::value, int >::type = 0 >\n                                        JSON_EXPLICIT operator ValueType() const\n    {\n        // delegate the call to get<>() const\n        return get<ValueType>();\n    }\n\n    /// @brief get a binary value\n    /// @sa https://json.nlohmann.me/api/basic_json/get_binary/\n    binary_t& get_binary()\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, detail::concat(\"type must be binary, but is \", type_name()), this));\n        }\n\n        return *get_ptr<binary_t*>();\n    }\n\n    /// @brief get a binary value\n    /// @sa https://json.nlohmann.me/api/basic_json/get_binary/\n    const binary_t& get_binary() const\n    {\n        if (!is_binary())\n        {\n            JSON_THROW(type_error::create(302, detail::concat(\"type must be binary, but is \", type_name()), this));\n        }\n\n        return *get_ptr<const binary_t*>();\n    }\n\n    /// @}\n\n\n    ////////////////////\n    // element access //\n    ////////////////////\n\n    /// @name element access\n    /// Access to the JSON value.\n    /// @{\n\n    /// @brief access specified array element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    reference at(size_type idx)\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return set_parent(m_value.array->at(idx));\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, detail::concat(\"array index \", std::to_string(idx), \" is out of range\"), this));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n    }\n\n    /// @brief access specified array element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    const_reference at(size_type idx) const\n    {\n        // at only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, detail::concat(\"array index \", std::to_string(idx), \" is out of range\"), this));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n    }\n\n    /// @brief access specified object element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    reference at(const typename object_t::key_type& key)\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n\n        auto it = m_value.object->find(key);\n        if (it == m_value.object->end())\n        {\n            JSON_THROW(out_of_range::create(403, detail::concat(\"key '\", key, \"' not found\"), this));\n        }\n        return set_parent(it->second);\n    }\n\n    /// @brief access specified object element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    reference at(KeyType && key)\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n\n        auto it = m_value.object->find(std::forward<KeyType>(key));\n        if (it == m_value.object->end())\n        {\n            JSON_THROW(out_of_range::create(403, detail::concat(\"key '\", string_t(std::forward<KeyType>(key)), \"' not found\"), this));\n        }\n        return set_parent(it->second);\n    }\n\n    /// @brief access specified object element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    const_reference at(const typename object_t::key_type& key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n\n        auto it = m_value.object->find(key);\n        if (it == m_value.object->end())\n        {\n            JSON_THROW(out_of_range::create(403, detail::concat(\"key '\", key, \"' not found\"), this));\n        }\n        return it->second;\n    }\n\n    /// @brief access specified object element with bounds checking\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    const_reference at(KeyType && key) const\n    {\n        // at only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(304, detail::concat(\"cannot use at() with \", type_name()), this));\n        }\n\n        auto it = m_value.object->find(std::forward<KeyType>(key));\n        if (it == m_value.object->end())\n        {\n            JSON_THROW(out_of_range::create(403, detail::concat(\"key '\", string_t(std::forward<KeyType>(key)), \"' not found\"), this));\n        }\n        return it->second;\n    }\n\n    /// @brief access specified array element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    reference operator[](size_type idx)\n    {\n        // implicitly convert null value to an empty array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value.array = create<array_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // fill up array with null values if given idx is outside range\n            if (idx >= m_value.array->size())\n            {\n#if JSON_DIAGNOSTICS\n                // remember array size & capacity before resizing\n                const auto old_size = m_value.array->size();\n                const auto old_capacity = m_value.array->capacity();\n#endif\n                m_value.array->resize(idx + 1);\n\n#if JSON_DIAGNOSTICS\n                if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity))\n                {\n                    // capacity has changed: update all parents\n                    set_parents();\n                }\n                else\n                {\n                    // set parent for values added above\n                    set_parents(begin() + static_cast<typename iterator::difference_type>(old_size), static_cast<typename iterator::difference_type>(idx + 1 - old_size));\n                }\n#endif\n                assert_invariant();\n            }\n\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a numeric argument with \", type_name()), this));\n    }\n\n    /// @brief access specified array element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    const_reference operator[](size_type idx) const\n    {\n        // const operator[] only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a numeric argument with \", type_name()), this));\n    }\n\n    /// @brief access specified object element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    reference operator[](typename object_t::key_type key)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            auto result = m_value.object->emplace(std::move(key), nullptr);\n            return set_parent(result.first->second);\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a string argument with \", type_name()), this));\n    }\n\n    /// @brief access specified object element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    const_reference operator[](const typename object_t::key_type& key) const\n    {\n        // const operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            auto it = m_value.object->find(key);\n            JSON_ASSERT(it != m_value.object->end());\n            return it->second;\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a string argument with \", type_name()), this));\n    }\n\n    // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC\n    // (they seemingly cannot be constrained to resolve the ambiguity)\n    template<typename T>\n    reference operator[](T* key)\n    {\n        return operator[](typename object_t::key_type(key));\n    }\n\n    template<typename T>\n    const_reference operator[](T* key) const\n    {\n        return operator[](typename object_t::key_type(key));\n    }\n\n    /// @brief access specified object element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >\n    reference operator[](KeyType && key)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            auto result = m_value.object->emplace(std::forward<KeyType>(key), nullptr);\n            return set_parent(result.first->second);\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a string argument with \", type_name()), this));\n    }\n\n    /// @brief access specified object element\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >\n    const_reference operator[](KeyType && key) const\n    {\n        // const operator[] only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            auto it = m_value.object->find(std::forward<KeyType>(key));\n            JSON_ASSERT(it != m_value.object->end());\n            return it->second;\n        }\n\n        JSON_THROW(type_error::create(305, detail::concat(\"cannot use operator[] with a string argument with \", type_name()), this));\n    }\n\n  private:\n    template<typename KeyType>\n    using is_comparable_with_object_key = detail::is_comparable <\n        object_comparator_t, const typename object_t::key_type&, KeyType >;\n\n    template<typename ValueType>\n    using value_return_type = std::conditional <\n        detail::is_c_string_uncvref<ValueType>::value,\n        string_t, typename std::decay<ValueType>::type >;\n\n  public:\n    /// @brief access specified object element with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, detail::enable_if_t <\n                   !detail::is_transparent<object_comparator_t>::value\n                   && detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if key is found, return value and given default value otherwise\n            const auto it = find(key);\n            if (it != end())\n            {\n                return it->template get<ValueType>();\n            }\n\n            return default_value;\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    /// @brief access specified object element with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,\n               detail::enable_if_t <\n                   !detail::is_transparent<object_comparator_t>::value\n                   && detail::is_getable<basic_json_t, ReturnType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ReturnType value(const typename object_t::key_type& key, ValueType && default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if key is found, return value and given default value otherwise\n            const auto it = find(key);\n            if (it != end())\n            {\n                return it->template get<ReturnType>();\n            }\n\n            return std::forward<ValueType>(default_value);\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    /// @brief access specified object element with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, class KeyType, detail::enable_if_t <\n                   detail::is_transparent<object_comparator_t>::value\n                   && !detail::is_json_pointer<KeyType>::value\n                   && is_comparable_with_object_key<KeyType>::value\n                   && detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ValueType value(KeyType && key, const ValueType& default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if key is found, return value and given default value otherwise\n            const auto it = find(std::forward<KeyType>(key));\n            if (it != end())\n            {\n                return it->template get<ValueType>();\n            }\n\n            return default_value;\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    /// @brief access specified object element via JSON Pointer with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, class KeyType, class ReturnType = typename value_return_type<ValueType>::type,\n               detail::enable_if_t <\n                   detail::is_transparent<object_comparator_t>::value\n                   && !detail::is_json_pointer<KeyType>::value\n                   && is_comparable_with_object_key<KeyType>::value\n                   && detail::is_getable<basic_json_t, ReturnType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ReturnType value(KeyType && key, ValueType && default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if key is found, return value and given default value otherwise\n            const auto it = find(std::forward<KeyType>(key));\n            if (it != end())\n            {\n                return it->template get<ReturnType>();\n            }\n\n            return std::forward<ValueType>(default_value);\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    /// @brief access specified object element via JSON Pointer with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, detail::enable_if_t <\n                   detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ValueType value(const json_pointer& ptr, const ValueType& default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if pointer resolves a value, return it or use default value\n            JSON_TRY\n            {\n                return ptr.get_checked(this).template get<ValueType>();\n            }\n            JSON_INTERNAL_CATCH (out_of_range&)\n            {\n                return default_value;\n            }\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    /// @brief access specified object element via JSON Pointer with default value\n    /// @sa https://json.nlohmann.me/api/basic_json/value/\n    template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,\n               detail::enable_if_t <\n                   detail::is_getable<basic_json_t, ReturnType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    ReturnType value(const json_pointer& ptr, ValueType && default_value) const\n    {\n        // value only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            // if pointer resolves a value, return it or use default value\n            JSON_TRY\n            {\n                return ptr.get_checked(this).template get<ReturnType>();\n            }\n            JSON_INTERNAL_CATCH (out_of_range&)\n            {\n                return std::forward<ValueType>(default_value);\n            }\n        }\n\n        JSON_THROW(type_error::create(306, detail::concat(\"cannot use value() with \", type_name()), this));\n    }\n\n    template < class ValueType, class BasicJsonType, detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value\n                   && detail::is_getable<basic_json_t, ValueType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const\n    {\n        return value(ptr.convert(), default_value);\n    }\n\n    template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,\n               detail::enable_if_t <\n                   detail::is_basic_json<BasicJsonType>::value\n                   && detail::is_getable<basic_json_t, ReturnType>::value\n                   && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    ReturnType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, ValueType && default_value) const\n    {\n        return value(ptr.convert(), std::forward<ValueType>(default_value));\n    }\n\n    /// @brief access the first element\n    /// @sa https://json.nlohmann.me/api/basic_json/front/\n    reference front()\n    {\n        return *begin();\n    }\n\n    /// @brief access the first element\n    /// @sa https://json.nlohmann.me/api/basic_json/front/\n    const_reference front() const\n    {\n        return *cbegin();\n    }\n\n    /// @brief access the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/back/\n    reference back()\n    {\n        auto tmp = end();\n        --tmp;\n        return *tmp;\n    }\n\n    /// @brief access the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/back/\n    const_reference back() const\n    {\n        auto tmp = cend();\n        --tmp;\n        return *tmp;\n    }\n\n    /// @brief remove element given an iterator\n    /// @sa https://json.nlohmann.me/api/basic_json/erase/\n    template < class IteratorType, detail::enable_if_t <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >\n    IteratorType erase(IteratorType pos)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", this));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))\n                {\n                    JSON_THROW(invalid_iterator::create(205, \"iterator out of range\", this));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);\n                    m_value.binary = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(type_error::create(307, detail::concat(\"cannot use erase() with \", type_name()), this));\n        }\n\n        return result;\n    }\n\n    /// @brief remove elements given an iterator range\n    /// @sa https://json.nlohmann.me/api/basic_json/erase/\n    template < class IteratorType, detail::enable_if_t <\n                   std::is_same<IteratorType, typename basic_json_t::iterator>::value ||\n                   std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >\n    IteratorType erase(IteratorType first, IteratorType last)\n    {\n        // make sure iterator fits the current value\n        if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(203, \"iterators do not fit current value\", this));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            case value_t::binary:\n            {\n                if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()\n                                       || !last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\", this));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n                else if (is_binary())\n                {\n                    AllocatorType<binary_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.binary);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.binary, 1);\n                    m_value.binary = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,\n                                              last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,\n                                             last.m_it.array_iterator);\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                JSON_THROW(type_error::create(307, detail::concat(\"cannot use erase() with \", type_name()), this));\n        }\n\n        return result;\n    }\n\n  private:\n    template < typename KeyType, detail::enable_if_t <\n                   detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >\n    size_type erase_internal(KeyType && key)\n    {\n        // this erase only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(307, detail::concat(\"cannot use erase() with \", type_name()), this));\n        }\n\n        return m_value.object->erase(std::forward<KeyType>(key));\n    }\n\n    template < typename KeyType, detail::enable_if_t <\n                   !detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >\n    size_type erase_internal(KeyType && key)\n    {\n        // this erase only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(307, detail::concat(\"cannot use erase() with \", type_name()), this));\n        }\n\n        const auto it = m_value.object->find(std::forward<KeyType>(key));\n        if (it != m_value.object->end())\n        {\n            m_value.object->erase(it);\n            return 1;\n        }\n        return 0;\n    }\n\n  public:\n\n    /// @brief remove element from a JSON object given a key\n    /// @sa https://json.nlohmann.me/api/basic_json/erase/\n    size_type erase(const typename object_t::key_type& key)\n    {\n        // the indirection via erase_internal() is added to avoid making this\n        // function a template and thus de-rank it during overload resolution\n        return erase_internal(key);\n    }\n\n    /// @brief remove element from a JSON object given a key\n    /// @sa https://json.nlohmann.me/api/basic_json/erase/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    size_type erase(KeyType && key)\n    {\n        return erase_internal(std::forward<KeyType>(key));\n    }\n\n    /// @brief remove element from a JSON array given an index\n    /// @sa https://json.nlohmann.me/api/basic_json/erase/\n    void erase(const size_type idx)\n    {\n        // this erase only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            if (JSON_HEDLEY_UNLIKELY(idx >= size()))\n            {\n                JSON_THROW(out_of_range::create(401, detail::concat(\"array index \", std::to_string(idx), \" is out of range\"), this));\n            }\n\n            m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));\n        }\n        else\n        {\n            JSON_THROW(type_error::create(307, detail::concat(\"cannot use erase() with \", type_name()), this));\n        }\n    }\n\n    /// @}\n\n\n    ////////////\n    // lookup //\n    ////////////\n\n    /// @name lookup\n    /// @{\n\n    /// @brief find an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/find/\n    iterator find(const typename object_t::key_type& key)\n    {\n        auto result = end();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(key);\n        }\n\n        return result;\n    }\n\n    /// @brief find an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/find/\n    const_iterator find(const typename object_t::key_type& key) const\n    {\n        auto result = cend();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(key);\n        }\n\n        return result;\n    }\n\n    /// @brief find an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/find/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    iterator find(KeyType && key)\n    {\n        auto result = end();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyType>(key));\n        }\n\n        return result;\n    }\n\n    /// @brief find an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/find/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    const_iterator find(KeyType && key) const\n    {\n        auto result = cend();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyType>(key));\n        }\n\n        return result;\n    }\n\n    /// @brief returns the number of occurrences of a key in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/count/\n    size_type count(const typename object_t::key_type& key) const\n    {\n        // return 0 for all nonobject types\n        return is_object() ? m_value.object->count(key) : 0;\n    }\n\n    /// @brief returns the number of occurrences of a key in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/count/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    size_type count(KeyType && key) const\n    {\n        // return 0 for all nonobject types\n        return is_object() ? m_value.object->count(std::forward<KeyType>(key)) : 0;\n    }\n\n    /// @brief check the existence of an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/contains/\n    bool contains(const typename object_t::key_type& key) const\n    {\n        return is_object() && m_value.object->find(key) != m_value.object->end();\n    }\n\n    /// @brief check the existence of an element in a JSON object\n    /// @sa https://json.nlohmann.me/api/basic_json/contains/\n    template<class KeyType, detail::enable_if_t<\n                 detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>\n    bool contains(KeyType && key) const\n    {\n        return is_object() && m_value.object->find(std::forward<KeyType>(key)) != m_value.object->end();\n    }\n\n    /// @brief check the existence of an element in a JSON object given a JSON pointer\n    /// @sa https://json.nlohmann.me/api/basic_json/contains/\n    bool contains(const json_pointer& ptr) const\n    {\n        return ptr.contains(this);\n    }\n\n    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    bool contains(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr) const\n    {\n        return ptr.contains(this);\n    }\n\n    /// @}\n\n\n    ///////////////\n    // iterators //\n    ///////////////\n\n    /// @name iterators\n    /// @{\n\n    /// @brief returns an iterator to the first element\n    /// @sa https://json.nlohmann.me/api/basic_json/begin/\n    iterator begin() noexcept\n    {\n        iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /// @brief returns an iterator to the first element\n    /// @sa https://json.nlohmann.me/api/basic_json/begin/\n    const_iterator begin() const noexcept\n    {\n        return cbegin();\n    }\n\n    /// @brief returns a const iterator to the first element\n    /// @sa https://json.nlohmann.me/api/basic_json/cbegin/\n    const_iterator cbegin() const noexcept\n    {\n        const_iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /// @brief returns an iterator to one past the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/end/\n    iterator end() noexcept\n    {\n        iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /// @brief returns an iterator to one past the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/end/\n    const_iterator end() const noexcept\n    {\n        return cend();\n    }\n\n    /// @brief returns an iterator to one past the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/cend/\n    const_iterator cend() const noexcept\n    {\n        const_iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /// @brief returns an iterator to the reverse-beginning\n    /// @sa https://json.nlohmann.me/api/basic_json/rbegin/\n    reverse_iterator rbegin() noexcept\n    {\n        return reverse_iterator(end());\n    }\n\n    /// @brief returns an iterator to the reverse-beginning\n    /// @sa https://json.nlohmann.me/api/basic_json/rbegin/\n    const_reverse_iterator rbegin() const noexcept\n    {\n        return crbegin();\n    }\n\n    /// @brief returns an iterator to the reverse-end\n    /// @sa https://json.nlohmann.me/api/basic_json/rend/\n    reverse_iterator rend() noexcept\n    {\n        return reverse_iterator(begin());\n    }\n\n    /// @brief returns an iterator to the reverse-end\n    /// @sa https://json.nlohmann.me/api/basic_json/rend/\n    const_reverse_iterator rend() const noexcept\n    {\n        return crend();\n    }\n\n    /// @brief returns a const reverse iterator to the last element\n    /// @sa https://json.nlohmann.me/api/basic_json/crbegin/\n    const_reverse_iterator crbegin() const noexcept\n    {\n        return const_reverse_iterator(cend());\n    }\n\n    /// @brief returns a const reverse iterator to one before the first\n    /// @sa https://json.nlohmann.me/api/basic_json/crend/\n    const_reverse_iterator crend() const noexcept\n    {\n        return const_reverse_iterator(cbegin());\n    }\n\n  public:\n    /// @brief wrapper to access iterator member functions in range-based for\n    /// @sa https://json.nlohmann.me/api/basic_json/items/\n    /// @deprecated This function is deprecated since 3.1.0 and will be removed in\n    ///             version 4.0.0 of the library. Please use @ref items() instead;\n    ///             that is, replace `json::iterator_wrapper(j)` with `j.items()`.\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /// @brief wrapper to access iterator member functions in range-based for\n    /// @sa https://json.nlohmann.me/api/basic_json/items/\n    /// @deprecated This function is deprecated since 3.1.0 and will be removed in\n    ///         version 4.0.0 of the library. Please use @ref items() instead;\n    ///         that is, replace `json::iterator_wrapper(j)` with `j.items()`.\n    JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())\n    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /// @brief helper to access iterator member functions in range-based for\n    /// @sa https://json.nlohmann.me/api/basic_json/items/\n    iteration_proxy<iterator> items() noexcept\n    {\n        return iteration_proxy<iterator>(*this);\n    }\n\n    /// @brief helper to access iterator member functions in range-based for\n    /// @sa https://json.nlohmann.me/api/basic_json/items/\n    iteration_proxy<const_iterator> items() const noexcept\n    {\n        return iteration_proxy<const_iterator>(*this);\n    }\n\n    /// @}\n\n\n    //////////////\n    // capacity //\n    //////////////\n\n    /// @name capacity\n    /// @{\n\n    /// @brief checks whether the container is empty.\n    /// @sa https://json.nlohmann.me/api/basic_json/empty/\n    bool empty() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return true;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::empty()\n                return m_value.array->empty();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::empty()\n                return m_value.object->empty();\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types are nonempty\n                return false;\n            }\n        }\n    }\n\n    /// @brief returns the number of elements\n    /// @sa https://json.nlohmann.me/api/basic_json/size/\n    size_type size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return 0;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::size()\n                return m_value.array->size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::size()\n                return m_value.object->size();\n            }\n\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types have size 1\n                return 1;\n            }\n        }\n    }\n\n    /// @brief returns the maximum possible number of elements\n    /// @sa https://json.nlohmann.me/api/basic_json/max_size/\n    size_type max_size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::array:\n            {\n                // delegate call to array_t::max_size()\n                return m_value.array->max_size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::max_size()\n                return m_value.object->max_size();\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // all other types have max_size() == size()\n                return size();\n            }\n        }\n    }\n\n    /// @}\n\n\n    ///////////////\n    // modifiers //\n    ///////////////\n\n    /// @name modifiers\n    /// @{\n\n    /// @brief clears the contents\n    /// @sa https://json.nlohmann.me/api/basic_json/clear/\n    void clear() noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = 0;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = 0;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = 0.0;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = false;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value.string->clear();\n                break;\n            }\n\n            case value_t::binary:\n            {\n                m_value.binary->clear();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array->clear();\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object->clear();\n                break;\n            }\n\n            case value_t::null:\n            case value_t::discarded:\n            default:\n                break;\n        }\n    }\n\n    /// @brief add an object to an array\n    /// @sa https://json.nlohmann.me/api/basic_json/push_back/\n    void push_back(basic_json&& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, detail::concat(\"cannot use push_back() with \", type_name()), this));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (move semantics)\n        const auto old_capacity = m_value.array->capacity();\n        m_value.array->push_back(std::move(val));\n        set_parent(m_value.array->back(), old_capacity);\n        // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor\n    }\n\n    /// @brief add an object to an array\n    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/\n    reference operator+=(basic_json&& val)\n    {\n        push_back(std::move(val));\n        return *this;\n    }\n\n    /// @brief add an object to an array\n    /// @sa https://json.nlohmann.me/api/basic_json/push_back/\n    void push_back(const basic_json& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(308, detail::concat(\"cannot use push_back() with \", type_name()), this));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array\n        const auto old_capacity = m_value.array->capacity();\n        m_value.array->push_back(val);\n        set_parent(m_value.array->back(), old_capacity);\n    }\n\n    /// @brief add an object to an array\n    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/\n    reference operator+=(const basic_json& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /// @brief add an object to an object\n    /// @sa https://json.nlohmann.me/api/basic_json/push_back/\n    void push_back(const typename object_t::value_type& val)\n    {\n        // push_back only works for null objects or objects\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(308, detail::concat(\"cannot use push_back() with \", type_name()), this));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to object\n        auto res = m_value.object->insert(val);\n        set_parent(res.first->second);\n    }\n\n    /// @brief add an object to an object\n    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/\n    reference operator+=(const typename object_t::value_type& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /// @brief add an object to an object\n    /// @sa https://json.nlohmann.me/api/basic_json/push_back/\n    void push_back(initializer_list_t init)\n    {\n        if (is_object() && init.size() == 2 && (*init.begin())->is_string())\n        {\n            basic_json&& key = init.begin()->moved_or_copied();\n            push_back(typename object_t::value_type(\n                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));\n        }\n        else\n        {\n            push_back(basic_json(init));\n        }\n    }\n\n    /// @brief add an object to an object\n    /// @sa https://json.nlohmann.me/api/basic_json/operator+=/\n    reference operator+=(initializer_list_t init)\n    {\n        push_back(init);\n        return *this;\n    }\n\n    /// @brief add an object to an array\n    /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/\n    template<class... Args>\n    reference emplace_back(Args&& ... args)\n    {\n        // emplace_back only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))\n        {\n            JSON_THROW(type_error::create(311, detail::concat(\"cannot use emplace_back() with \", type_name()), this));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n        const auto old_capacity = m_value.array->capacity();\n        m_value.array->emplace_back(std::forward<Args>(args)...);\n        return set_parent(m_value.array->back(), old_capacity);\n    }\n\n    /// @brief add an object to an object if key does not exist\n    /// @sa https://json.nlohmann.me/api/basic_json/emplace/\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&& ... args)\n    {\n        // emplace only works for null objects or arrays\n        if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))\n        {\n            JSON_THROW(type_error::create(311, detail::concat(\"cannot use emplace() with \", type_name()), this));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n        auto res = m_value.object->emplace(std::forward<Args>(args)...);\n        set_parent(res.first->second);\n\n        // create result iterator and set iterator to the result of emplace\n        auto it = begin();\n        it.m_it.object_iterator = res.first;\n\n        // return pair of iterator and boolean\n        return {it, res.second};\n    }\n\n    /// Helper for insertion of an iterator\n    /// @note: This uses std::distance to support GCC 4.8,\n    ///        see https://github.com/nlohmann/json/pull/1257\n    template<typename... Args>\n    iterator insert_iterator(const_iterator pos, Args&& ... args)\n    {\n        iterator result(this);\n        JSON_ASSERT(m_value.array != nullptr);\n\n        auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);\n        m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);\n        result.m_it.array_iterator = m_value.array->begin() + insert_pos;\n\n        // This could have been written as:\n        // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);\n        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.\n\n        set_parents();\n        return result;\n    }\n\n    /// @brief inserts element into array\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    iterator insert(const_iterator pos, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", this));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, val);\n        }\n\n        JSON_THROW(type_error::create(309, detail::concat(\"cannot use insert() with \", type_name()), this));\n    }\n\n    /// @brief inserts element into array\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    iterator insert(const_iterator pos, basic_json&& val)\n    {\n        return insert(pos, val);\n    }\n\n    /// @brief inserts copies of element into array\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    iterator insert(const_iterator pos, size_type cnt, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", this));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, cnt, val);\n        }\n\n        JSON_THROW(type_error::create(309, detail::concat(\"cannot use insert() with \", type_name()), this));\n    }\n\n    /// @brief inserts range of elements into array\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    iterator insert(const_iterator pos, const_iterator first, const_iterator last)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, detail::concat(\"cannot use insert() with \", type_name()), this));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", this));\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(first.m_object == this))\n        {\n            JSON_THROW(invalid_iterator::create(211, \"passed iterators may not belong to container\", this));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);\n    }\n\n    /// @brief inserts elements from initializer list into array\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    iterator insert(const_iterator pos, initializer_list_t ilist)\n    {\n        // insert only works for arrays\n        if (JSON_HEDLEY_UNLIKELY(!is_array()))\n        {\n            JSON_THROW(type_error::create(309, detail::concat(\"cannot use insert() with \", type_name()), this));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\", this));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, ilist.begin(), ilist.end());\n    }\n\n    /// @brief inserts range of elements into object\n    /// @sa https://json.nlohmann.me/api/basic_json/insert/\n    void insert(const_iterator first, const_iterator last)\n    {\n        // insert only works for objects\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(309, detail::concat(\"cannot use insert() with \", type_name()), this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", this));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\", this));\n        }\n\n        m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);\n    }\n\n    /// @brief updates a JSON object from another object, overwriting existing keys\n    /// @sa https://json.nlohmann.me/api/basic_json/update/\n    void update(const_reference j, bool merge_objects = false)\n    {\n        update(j.begin(), j.end(), merge_objects);\n    }\n\n    /// @brief updates a JSON object from another object, overwriting existing keys\n    /// @sa https://json.nlohmann.me/api/basic_json/update/\n    void update(const_iterator first, const_iterator last, bool merge_objects = false)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_HEDLEY_UNLIKELY(!is_object()))\n        {\n            JSON_THROW(type_error::create(312, detail::concat(\"cannot use update() with \", type_name()), this));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\", this));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))\n        {\n            JSON_THROW(type_error::create(312, detail::concat(\"cannot use update() with \", first.m_object->type_name()), first.m_object));\n        }\n\n        for (auto it = first; it != last; ++it)\n        {\n            if (merge_objects && it.value().is_object())\n            {\n                auto it2 = m_value.object->find(it.key());\n                if (it2 != m_value.object->end())\n                {\n                    it2->second.update(it.value(), true);\n                    continue;\n                }\n            }\n            m_value.object->operator[](it.key()) = it.value();\n#if JSON_DIAGNOSTICS\n            m_value.object->operator[](it.key()).m_parent = this;\n#endif\n        }\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(reference other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        std::swap(m_type, other.m_type);\n        std::swap(m_value, other.m_value);\n\n        set_parents();\n        other.set_parents();\n        assert_invariant();\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    friend void swap(reference left, reference right) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value&&\n        std::is_nothrow_move_assignable<value_t>::value&&\n        std::is_nothrow_move_constructible<json_value>::value&&\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        left.swap(right);\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(array_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for arrays\n        if (JSON_HEDLEY_LIKELY(is_array()))\n        {\n            using std::swap;\n            swap(*(m_value.array), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, detail::concat(\"cannot use swap(array_t&) with \", type_name()), this));\n        }\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(object_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for objects\n        if (JSON_HEDLEY_LIKELY(is_object()))\n        {\n            using std::swap;\n            swap(*(m_value.object), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, detail::concat(\"cannot use swap(object_t&) with \", type_name()), this));\n        }\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(string_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_string()))\n        {\n            using std::swap;\n            swap(*(m_value.string), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, detail::concat(\"cannot use swap(string_t&) with \", type_name()), this));\n        }\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(binary_t& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            using std::swap;\n            swap(*(m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, detail::concat(\"cannot use swap(binary_t&) with \", type_name()), this));\n        }\n    }\n\n    /// @brief exchanges the values\n    /// @sa https://json.nlohmann.me/api/basic_json/swap/\n    void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)\n    {\n        // swap only works for strings\n        if (JSON_HEDLEY_LIKELY(is_binary()))\n        {\n            using std::swap;\n            swap(*(m_value.binary), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, detail::concat(\"cannot use swap(binary_t::container_type&) with \", type_name()), this));\n        }\n    }\n\n    /// @}\n\n    //////////////////////////////////////////\n    // lexicographical comparison operators //\n    //////////////////////////////////////////\n\n    /// @name lexicographical comparison operators\n    /// @{\n\n    // note parentheses around operands are necessary; see\n    // https://github.com/nlohmann/json/issues/1530\n#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result)                       \\\n    const auto lhs_type = lhs.type();                                                                    \\\n    const auto rhs_type = rhs.type();                                                                    \\\n    \\\n    if (lhs_type == rhs_type) /* NOLINT(readability/braces) */                                           \\\n    {                                                                                                    \\\n        switch (lhs_type)                                                                                \\\n        {                                                                                                \\\n            case value_t::array:                                                                         \\\n                return (*lhs.m_value.array) op (*rhs.m_value.array);                                     \\\n                \\\n            case value_t::object:                                                                        \\\n                return (*lhs.m_value.object) op (*rhs.m_value.object);                                   \\\n                \\\n            case value_t::null:                                                                          \\\n                return (null_result);                                                                    \\\n                \\\n            case value_t::string:                                                                        \\\n                return (*lhs.m_value.string) op (*rhs.m_value.string);                                   \\\n                \\\n            case value_t::boolean:                                                                       \\\n                return (lhs.m_value.boolean) op (rhs.m_value.boolean);                                   \\\n                \\\n            case value_t::number_integer:                                                                \\\n                return (lhs.m_value.number_integer) op (rhs.m_value.number_integer);                     \\\n                \\\n            case value_t::number_unsigned:                                                               \\\n                return (lhs.m_value.number_unsigned) op (rhs.m_value.number_unsigned);                   \\\n                \\\n            case value_t::number_float:                                                                  \\\n                return (lhs.m_value.number_float) op (rhs.m_value.number_float);                         \\\n                \\\n            case value_t::binary:                                                                        \\\n                return (*lhs.m_value.binary) op (*rhs.m_value.binary);                                   \\\n                \\\n            case value_t::discarded:                                                                     \\\n            default:                                                                                     \\\n                return (unordered_result);                                                               \\\n        }                                                                                                \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float)                   \\\n    {                                                                                                    \\\n        return static_cast<number_float_t>(lhs.m_value.number_integer) op rhs.m_value.number_float;      \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer)                   \\\n    {                                                                                                    \\\n        return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_integer);      \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float)                  \\\n    {                                                                                                    \\\n        return static_cast<number_float_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_float;     \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned)                  \\\n    {                                                                                                    \\\n        return lhs.m_value.number_float op static_cast<number_float_t>(rhs.m_value.number_unsigned);     \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer)                \\\n    {                                                                                                    \\\n        return static_cast<number_integer_t>(lhs.m_value.number_unsigned) op rhs.m_value.number_integer; \\\n    }                                                                                                    \\\n    else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned)                \\\n    {                                                                                                    \\\n        return lhs.m_value.number_integer op static_cast<number_integer_t>(rhs.m_value.number_unsigned); \\\n    }                                                                                                    \\\n    else if(compares_unordered(lhs, rhs))\\\n    {\\\n        return (unordered_result);\\\n    }\\\n    \\\n    return (default_result);\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    // returns true if:\n    // - any operand is NaN and the other operand is of number type\n    // - any operand is discarded\n    // in legacy mode, discarded values are considered ordered if\n    // an operation is computed as an odd number of inverses of others\n    static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept\n    {\n        if ((lhs.is_number_float() && std::isnan(lhs.m_value.number_float) && rhs.is_number())\n                || (rhs.is_number_float() && std::isnan(rhs.m_value.number_float) && lhs.is_number()))\n        {\n            return true;\n        }\n#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON\n        return (lhs.is_discarded() || rhs.is_discarded()) && !inverse;\n#else\n        static_cast<void>(inverse);\n        return lhs.is_discarded() || rhs.is_discarded();\n#endif\n    }\n\n  private:\n    bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept\n    {\n        return compares_unordered(*this, rhs, inverse);\n    }\n\n  public:\n#if JSON_HAS_THREE_WAY_COMPARISON\n    /// @brief comparison: equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/\n    bool operator==(const_reference rhs) const noexcept\n    {\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n        const_reference lhs = *this;\n        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n    }\n\n    /// @brief comparison: equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/\n    template<typename ScalarType>\n    requires std::is_scalar_v<ScalarType>\n    bool operator==(ScalarType rhs) const noexcept\n    {\n        return *this == basic_json(rhs);\n    }\n\n    /// @brief comparison: not equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/\n    bool operator!=(const_reference rhs) const noexcept\n    {\n        if (compares_unordered(rhs, true))\n        {\n            return false;\n        }\n        return !operator==(rhs);\n    }\n\n    /// @brief comparison: 3-way\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/\n    std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*\n    {\n        const_reference lhs = *this;\n        // default_result is used if we cannot compare values. In that case,\n        // we compare types.\n        JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*\n                                std::partial_ordering::equivalent,\n                                std::partial_ordering::unordered,\n                                lhs_type <=> rhs_type) // *NOPAD*\n    }\n\n    /// @brief comparison: 3-way\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/\n    template<typename ScalarType>\n    requires std::is_scalar_v<ScalarType>\n    std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD*\n    {\n        return *this <=> basic_json(rhs); // *NOPAD*\n    }\n\n#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON\n    // all operators that are computed as an odd number of inverses of others\n    // need to be overloaded to emulate the legacy comparison behavior\n\n    /// @brief comparison: less than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)\n    bool operator<=(const_reference rhs) const noexcept\n    {\n        if (compares_unordered(rhs, true))\n        {\n            return false;\n        }\n        return !(rhs < *this);\n    }\n\n    /// @brief comparison: less than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/\n    template<typename ScalarType>\n    requires std::is_scalar_v<ScalarType>\n    bool operator<=(ScalarType rhs) const noexcept\n    {\n        return *this <= basic_json(rhs);\n    }\n\n    /// @brief comparison: greater than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)\n    bool operator>=(const_reference rhs) const noexcept\n    {\n        if (compares_unordered(rhs, true))\n        {\n            return false;\n        }\n        return !(*this < rhs);\n    }\n\n    /// @brief comparison: greater than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/\n    template<typename ScalarType>\n    requires std::is_scalar_v<ScalarType>\n    bool operator>=(ScalarType rhs) const noexcept\n    {\n        return *this >= basic_json(rhs);\n    }\n#endif\n#else\n    /// @brief comparison: equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/\n    friend bool operator==(const_reference lhs, const_reference rhs) noexcept\n    {\n#ifdef __GNUC__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n        JSON_IMPLEMENT_OPERATOR( ==, true, false, false)\n#ifdef __GNUC__\n#pragma GCC diagnostic pop\n#endif\n    }\n\n    /// @brief comparison: equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs == basic_json(rhs);\n    }\n\n    /// @brief comparison: equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) == rhs;\n    }\n\n    /// @brief comparison: not equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/\n    friend bool operator!=(const_reference lhs, const_reference rhs) noexcept\n    {\n        if (compares_unordered(lhs, rhs, true))\n        {\n            return false;\n        }\n        return !(lhs == rhs);\n    }\n\n    /// @brief comparison: not equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs != basic_json(rhs);\n    }\n\n    /// @brief comparison: not equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) != rhs;\n    }\n\n    /// @brief comparison: less than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/\n    friend bool operator<(const_reference lhs, const_reference rhs) noexcept\n    {\n        // default_result is used if we cannot compare values. In that case,\n        // we compare types. Note we have to call the operator explicitly,\n        // because MSVC has problems otherwise.\n        JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))\n    }\n\n    /// @brief comparison: less than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs < basic_json(rhs);\n    }\n\n    /// @brief comparison: less than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) < rhs;\n    }\n\n    /// @brief comparison: less than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/\n    friend bool operator<=(const_reference lhs, const_reference rhs) noexcept\n    {\n        if (compares_unordered(lhs, rhs, true))\n        {\n            return false;\n        }\n        return !(rhs < lhs);\n    }\n\n    /// @brief comparison: less than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs <= basic_json(rhs);\n    }\n\n    /// @brief comparison: less than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_le/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) <= rhs;\n    }\n\n    /// @brief comparison: greater than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/\n    friend bool operator>(const_reference lhs, const_reference rhs) noexcept\n    {\n        // double inverse\n        if (compares_unordered(lhs, rhs))\n        {\n            return false;\n        }\n        return !(lhs <= rhs);\n    }\n\n    /// @brief comparison: greater than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs > basic_json(rhs);\n    }\n\n    /// @brief comparison: greater than\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) > rhs;\n    }\n\n    /// @brief comparison: greater than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/\n    friend bool operator>=(const_reference lhs, const_reference rhs) noexcept\n    {\n        if (compares_unordered(lhs, rhs, true))\n        {\n            return false;\n        }\n        return !(lhs < rhs);\n    }\n\n    /// @brief comparison: greater than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept\n    {\n        return lhs >= basic_json(rhs);\n    }\n\n    /// @brief comparison: greater than or equal\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept\n    {\n        return basic_json(lhs) >= rhs;\n    }\n#endif\n\n#undef JSON_IMPLEMENT_OPERATOR\n\n    /// @}\n\n    ///////////////////\n    // serialization //\n    ///////////////////\n\n    /// @name serialization\n    /// @{\n#ifndef JSON_NO_IO\n    /// @brief serialize to stream\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/\n    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)\n    {\n        // read width member and use it as indentation parameter if nonzero\n        const bool pretty_print = o.width() > 0;\n        const auto indentation = pretty_print ? o.width() : 0;\n\n        // reset width to 0 for subsequent calls to this stream\n        o.width(0);\n\n        // do the actual serialization\n        serializer s(detail::output_adapter<char>(o), o.fill());\n        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));\n        return o;\n    }\n\n    /// @brief serialize to stream\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/\n    /// @deprecated This function is deprecated since 3.0.0 and will be removed in\n    ///             version 4.0.0 of the library. Please use\n    ///             operator<<(std::ostream&, const basic_json&) instead; that is,\n    ///             replace calls like `j >> o;` with `o << j;`.\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&))\n    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)\n    {\n        return o << j;\n    }\n#endif  // JSON_NO_IO\n    /// @}\n\n\n    /////////////////////\n    // deserialization //\n    /////////////////////\n\n    /// @name deserialization\n    /// @{\n\n    /// @brief deserialize from a compatible input\n    /// @sa https://json.nlohmann.me/api/basic_json/parse/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(InputType&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    /// @brief deserialize from a pair of character iterators\n    /// @sa https://json.nlohmann.me/api/basic_json/parse/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json parse(IteratorType first,\n                            IteratorType last,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))\n    static basic_json parse(detail::span_input_adapter&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true,\n                            const bool ignore_comments = false)\n    {\n        basic_json result;\n        parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result);\n        return result;\n    }\n\n    /// @brief check if the input is valid JSON\n    /// @sa https://json.nlohmann.me/api/basic_json/accept/\n    template<typename InputType>\n    static bool accept(InputType&& i,\n                       const bool ignore_comments = false)\n    {\n        return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);\n    }\n\n    /// @brief check if the input is valid JSON\n    /// @sa https://json.nlohmann.me/api/basic_json/accept/\n    template<typename IteratorType>\n    static bool accept(IteratorType first, IteratorType last,\n                       const bool ignore_comments = false)\n    {\n        return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))\n    static bool accept(detail::span_input_adapter&& i,\n                       const bool ignore_comments = false)\n    {\n        return parser(i.get(), nullptr, false, ignore_comments).accept(true);\n    }\n\n    /// @brief generate SAX events\n    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/\n    template <typename InputType, typename SAX>\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(InputType&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);\n    }\n\n    /// @brief generate SAX events\n    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/\n    template<class IteratorType, class SAX>\n    JSON_HEDLEY_NON_NULL(3)\n    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        return format == input_format_t::json\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);\n    }\n\n    /// @brief generate SAX events\n    /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/\n    /// @deprecated This function is deprecated since 3.8.0 and will be removed in\n    ///             version 4.0.0 of the library. Please use\n    ///             sax_parse(ptr, ptr + len) instead.\n    template <typename SAX>\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))\n    JSON_HEDLEY_NON_NULL(2)\n    static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true,\n                          const bool ignore_comments = false)\n    {\n        auto ia = i.get();\n        return format == input_format_t::json\n               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n               ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)\n               // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n               : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);\n    }\n#ifndef JSON_NO_IO\n    /// @brief deserialize from stream\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/\n    /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in\n    ///             version 4.0.0 of the library. Please use\n    ///             operator>>(std::istream&, basic_json&) instead; that is,\n    ///             replace calls like `j << i;` with `i >> j;`.\n    JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&))\n    friend std::istream& operator<<(basic_json& j, std::istream& i)\n    {\n        return operator>>(i, j);\n    }\n\n    /// @brief deserialize from stream\n    /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/\n    friend std::istream& operator>>(std::istream& i, basic_json& j)\n    {\n        parser(detail::input_adapter(i)).parse(false, j);\n        return i;\n    }\n#endif  // JSON_NO_IO\n    /// @}\n\n    ///////////////////////////\n    // convenience functions //\n    ///////////////////////////\n\n    /// @brief return the type as string\n    /// @sa https://json.nlohmann.me/api/basic_json/type_name/\n    JSON_HEDLEY_RETURNS_NON_NULL\n    const char* type_name() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n                return \"null\";\n            case value_t::object:\n                return \"object\";\n            case value_t::array:\n                return \"array\";\n            case value_t::string:\n                return \"string\";\n            case value_t::boolean:\n                return \"boolean\";\n            case value_t::binary:\n                return \"binary\";\n            case value_t::discarded:\n                return \"discarded\";\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            default:\n                return \"number\";\n        }\n    }\n\n\n  JSON_PRIVATE_UNLESS_TESTED:\n    //////////////////////\n    // member variables //\n    //////////////////////\n\n    /// the type of the current element\n    value_t m_type = value_t::null;\n\n    /// the value of the current element\n    json_value m_value = {};\n\n#if JSON_DIAGNOSTICS\n    /// a pointer to a parent value (for debugging purposes)\n    basic_json* m_parent = nullptr;\n#endif\n\n    //////////////////////////////////////////\n    // binary serialization/deserialization //\n    //////////////////////////////////////////\n\n    /// @name binary serialization/deserialization support\n    /// @{\n\n  public:\n    /// @brief create a CBOR serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/\n    static std::vector<std::uint8_t> to_cbor(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_cbor(j, result);\n        return result;\n    }\n\n    /// @brief create a CBOR serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/\n    static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_cbor(j);\n    }\n\n    /// @brief create a CBOR serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/\n    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_cbor(j);\n    }\n\n    /// @brief create a MessagePack serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/\n    static std::vector<std::uint8_t> to_msgpack(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_msgpack(j, result);\n        return result;\n    }\n\n    /// @brief create a MessagePack serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/\n    static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_msgpack(j);\n    }\n\n    /// @brief create a MessagePack serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/\n    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_msgpack(j);\n    }\n\n    /// @brief create a UBJSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/\n    static std::vector<std::uint8_t> to_ubjson(const basic_json& j,\n            const bool use_size = false,\n            const bool use_type = false)\n    {\n        std::vector<std::uint8_t> result;\n        to_ubjson(j, result, use_size, use_type);\n        return result;\n    }\n\n    /// @brief create a UBJSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/\n    static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type);\n    }\n\n    /// @brief create a UBJSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/\n    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<char>(o).write_ubjson(j, use_size, use_type);\n    }\n\n    /// @brief create a BJData serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/\n    static std::vector<std::uint8_t> to_bjdata(const basic_json& j,\n            const bool use_size = false,\n            const bool use_type = false)\n    {\n        std::vector<std::uint8_t> result;\n        to_bjdata(j, result, use_size, use_type);\n        return result;\n    }\n\n    /// @brief create a BJData serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/\n    static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true);\n    }\n\n    /// @brief create a BJData serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/\n    static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true);\n    }\n\n    /// @brief create a BSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/\n    static std::vector<std::uint8_t> to_bson(const basic_json& j)\n    {\n        std::vector<std::uint8_t> result;\n        to_bson(j, result);\n        return result;\n    }\n\n    /// @brief create a BSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/\n    static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o)\n    {\n        binary_writer<std::uint8_t>(o).write_bson(j);\n    }\n\n    /// @brief create a BSON serialization of a given JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/to_bson/\n    static void to_bson(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_bson(j);\n    }\n\n    /// @brief create a JSON value from an input in CBOR format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in CBOR format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_cbor(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);\n    }\n\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))\n    static basic_json from_cbor(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true,\n                                const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in MessagePack format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(InputType&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in MessagePack format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_msgpack(IteratorType first, IteratorType last,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(const T* ptr, std::size_t len,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        return from_msgpack(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))\n    static basic_json from_msgpack(detail::span_input_adapter&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in UBJSON format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(InputType&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in UBJSON format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_ubjson(IteratorType first, IteratorType last,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(const T* ptr, std::size_t len,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        return from_ubjson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))\n    static basic_json from_ubjson(detail::span_input_adapter&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n    /// @brief create a JSON value from an input in BJData format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bjdata(InputType&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in BJData format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bjdata(IteratorType first, IteratorType last,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in BSON format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_bson/\n    template<typename InputType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(InputType&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::forward<InputType>(i));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /// @brief create a JSON value from an input in BSON format\n    /// @sa https://json.nlohmann.me/api/basic_json/from_bson/\n    template<typename IteratorType>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json from_bson(IteratorType first, IteratorType last,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = detail::input_adapter(std::move(first), std::move(last));\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    template<typename T>\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(const T* ptr, std::size_t len,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        return from_bson(ptr, ptr + len, strict, allow_exceptions);\n    }\n\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))\n    static basic_json from_bson(detail::span_input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        auto ia = i.get();\n        // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)\n        const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n    /// @}\n\n    //////////////////////////\n    // JSON Pointer support //\n    //////////////////////////\n\n    /// @name JSON Pointer functions\n    /// @{\n\n    /// @brief access specified element via JSON Pointer\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    reference operator[](const json_pointer& ptr)\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /// @brief access specified element via JSON Pointer\n    /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/\n    const_reference operator[](const json_pointer& ptr) const\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /// @brief access specified element via JSON Pointer\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    reference at(const json_pointer& ptr)\n    {\n        return ptr.get_checked(this);\n    }\n\n    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)\n    {\n        return ptr.get_checked(this);\n    }\n\n    /// @brief access specified element via JSON Pointer\n    /// @sa https://json.nlohmann.me/api/basic_json/at/\n    const_reference at(const json_pointer& ptr) const\n    {\n        return ptr.get_checked(this);\n    }\n\n    template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)\n    const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const\n    {\n        return ptr.get_checked(this);\n    }\n\n    /// @brief return flattened JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/flatten/\n    basic_json flatten() const\n    {\n        basic_json result(value_t::object);\n        json_pointer::flatten(\"\", *this, result);\n        return result;\n    }\n\n    /// @brief unflatten a previously flattened JSON value\n    /// @sa https://json.nlohmann.me/api/basic_json/unflatten/\n    basic_json unflatten() const\n    {\n        return json_pointer::unflatten(*this);\n    }\n\n    /// @}\n\n    //////////////////////////\n    // JSON Patch functions //\n    //////////////////////////\n\n    /// @name JSON Patch functions\n    /// @{\n\n    /// @brief applies a JSON patch in-place without copying the object\n    /// @sa https://json.nlohmann.me/api/basic_json/patch/\n    void patch_inplace(const basic_json& json_patch)\n    {\n        basic_json& result = *this;\n        // the valid JSON Patch operations\n        enum class patch_operations {add, remove, replace, move, copy, test, invalid};\n\n        const auto get_op = [](const std::string & op)\n        {\n            if (op == \"add\")\n            {\n                return patch_operations::add;\n            }\n            if (op == \"remove\")\n            {\n                return patch_operations::remove;\n            }\n            if (op == \"replace\")\n            {\n                return patch_operations::replace;\n            }\n            if (op == \"move\")\n            {\n                return patch_operations::move;\n            }\n            if (op == \"copy\")\n            {\n                return patch_operations::copy;\n            }\n            if (op == \"test\")\n            {\n                return patch_operations::test;\n            }\n\n            return patch_operations::invalid;\n        };\n\n        // wrapper for \"add\" operation; add value at ptr\n        const auto operation_add = [&result](json_pointer & ptr, basic_json val)\n        {\n            // adding to the root of the target document means replacing it\n            if (ptr.empty())\n            {\n                result = val;\n                return;\n            }\n\n            // make sure the top element of the pointer exists\n            json_pointer top_pointer = ptr.top();\n            if (top_pointer != ptr)\n            {\n                result.at(top_pointer);\n            }\n\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            // parent must exist when performing patch add per RFC6902 specs\n            basic_json& parent = result.at(ptr);\n\n            switch (parent.m_type)\n            {\n                case value_t::null:\n                case value_t::object:\n                {\n                    // use operator[] to add value\n                    parent[last_path] = val;\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    if (last_path == \"-\")\n                    {\n                        // special case: append to back\n                        parent.push_back(val);\n                    }\n                    else\n                    {\n                        const auto idx = json_pointer::template array_index<basic_json_t>(last_path);\n                        if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))\n                        {\n                            // avoid undefined behavior\n                            JSON_THROW(out_of_range::create(401, detail::concat(\"array index \", std::to_string(idx), \" is out of range\"), &parent));\n                        }\n\n                        // default case: insert add offset\n                        parent.insert(parent.begin() + static_cast<difference_type>(idx), val);\n                    }\n                    break;\n                }\n\n                // if there exists a parent it cannot be primitive\n                case value_t::string: // LCOV_EXCL_LINE\n                case value_t::boolean: // LCOV_EXCL_LINE\n                case value_t::number_integer: // LCOV_EXCL_LINE\n                case value_t::number_unsigned: // LCOV_EXCL_LINE\n                case value_t::number_float: // LCOV_EXCL_LINE\n                case value_t::binary: // LCOV_EXCL_LINE\n                case value_t::discarded: // LCOV_EXCL_LINE\n                default:            // LCOV_EXCL_LINE\n                    JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE\n            }\n        };\n\n        // wrapper for \"remove\" operation; remove value at ptr\n        const auto operation_remove = [this, &result](json_pointer & ptr)\n        {\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.back();\n            ptr.pop_back();\n            basic_json& parent = result.at(ptr);\n\n            // remove child\n            if (parent.is_object())\n            {\n                // perform range check\n                auto it = parent.find(last_path);\n                if (JSON_HEDLEY_LIKELY(it != parent.end()))\n                {\n                    parent.erase(it);\n                }\n                else\n                {\n                    JSON_THROW(out_of_range::create(403, detail::concat(\"key '\", last_path, \"' not found\"), this));\n                }\n            }\n            else if (parent.is_array())\n            {\n                // note erase performs range check\n                parent.erase(json_pointer::template array_index<basic_json_t>(last_path));\n            }\n        };\n\n        // type check: top level value must be an array\n        if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))\n        {\n            JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\", &json_patch));\n        }\n\n        // iterate and apply the operations\n        for (const auto& val : json_patch)\n        {\n            // wrapper to get a value for an operation\n            const auto get_value = [&val](const std::string & op,\n                                          const std::string & member,\n                                          bool string_type) -> basic_json &\n            {\n                // find value\n                auto it = val.m_value.object->find(member);\n\n                // context-sensitive error message\n                const auto error_msg = (op == \"op\") ? \"operation\" : detail::concat(\"operation '\", op, '\\'');\n\n                // check if desired value is present\n                if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))\n                {\n                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n                    JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, \" must have member '\", member, \"'\"), &val));\n                }\n\n                // check if result is of type string\n                if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))\n                {\n                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n                    JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, \" must have string member '\", member, \"'\"), &val));\n                }\n\n                // no error: return value\n                return it->second;\n            };\n\n            // type check: every element of the array must be an object\n            if (JSON_HEDLEY_UNLIKELY(!val.is_object()))\n            {\n                JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\", &val));\n            }\n\n            // collect mandatory members\n            const auto op = get_value(\"op\", \"op\", true).template get<std::string>();\n            const auto path = get_value(op, \"path\", true).template get<std::string>();\n            json_pointer ptr(path);\n\n            switch (get_op(op))\n            {\n                case patch_operations::add:\n                {\n                    operation_add(ptr, get_value(\"add\", \"value\", false));\n                    break;\n                }\n\n                case patch_operations::remove:\n                {\n                    operation_remove(ptr);\n                    break;\n                }\n\n                case patch_operations::replace:\n                {\n                    // the \"path\" location must exist - use at()\n                    result.at(ptr) = get_value(\"replace\", \"value\", false);\n                    break;\n                }\n\n                case patch_operations::move:\n                {\n                    const auto from_path = get_value(\"move\", \"from\", true).template get<std::string>();\n                    json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The move operation is functionally identical to a\n                    // \"remove\" operation on the \"from\" location, followed\n                    // immediately by an \"add\" operation at the target\n                    // location with the value that was just removed.\n                    operation_remove(from_ptr);\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::copy:\n                {\n                    const auto from_path = get_value(\"copy\", \"from\", true).template get<std::string>();\n                    const json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The copy is functionally identical to an \"add\"\n                    // operation at the target location using the value\n                    // specified in the \"from\" member.\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::test:\n                {\n                    bool success = false;\n                    JSON_TRY\n                    {\n                        // check if \"value\" matches the one at \"path\"\n                        // the \"path\" location must exist - use at()\n                        success = (result.at(ptr) == get_value(\"test\", \"value\", false));\n                    }\n                    JSON_INTERNAL_CATCH (out_of_range&)\n                    {\n                        // ignore out of range errors: success remains false\n                    }\n\n                    // throw an exception if test fails\n                    if (JSON_HEDLEY_UNLIKELY(!success))\n                    {\n                        JSON_THROW(other_error::create(501, detail::concat(\"unsuccessful: \", val.dump()), &val));\n                    }\n\n                    break;\n                }\n\n                case patch_operations::invalid:\n                default:\n                {\n                    // op must be \"add\", \"remove\", \"replace\", \"move\", \"copy\", or\n                    // \"test\"\n                    JSON_THROW(parse_error::create(105, 0, detail::concat(\"operation value '\", op, \"' is invalid\"), &val));\n                }\n            }\n        }\n    }\n\n    /// @brief applies a JSON patch to a copy of the current object\n    /// @sa https://json.nlohmann.me/api/basic_json/patch/\n    basic_json patch(const basic_json& json_patch) const\n    {\n        basic_json result = *this;\n        result.patch_inplace(json_patch);\n        return result;\n    }\n\n    /// @brief creates a diff as a JSON patch\n    /// @sa https://json.nlohmann.me/api/basic_json/diff/\n    JSON_HEDLEY_WARN_UNUSED_RESULT\n    static basic_json diff(const basic_json& source, const basic_json& target,\n                           const std::string& path = \"\")\n    {\n        // the patch\n        basic_json result(value_t::array);\n\n        // if the values are the same, return empty patch\n        if (source == target)\n        {\n            return result;\n        }\n\n        if (source.type() != target.type())\n        {\n            // different types: replace value\n            result.push_back(\n            {\n                {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n            });\n            return result;\n        }\n\n        switch (source.type())\n        {\n            case value_t::array:\n            {\n                // first pass: traverse common elements\n                std::size_t i = 0;\n                while (i < source.size() && i < target.size())\n                {\n                    // recursive call to compare array values at index i\n                    auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i)));\n                    result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    ++i;\n                }\n\n                // We now reached the end of at least one array\n                // in a second pass, traverse the remaining elements\n\n                // remove my remaining elements\n                const auto end_index = static_cast<difference_type>(result.size());\n                while (i < source.size())\n                {\n                    // add operations in reverse order to avoid invalid\n                    // indices\n                    result.insert(result.begin() + end_index, object(\n                    {\n                        {\"op\", \"remove\"},\n                        {\"path\", detail::concat(path, '/', std::to_string(i))}\n                    }));\n                    ++i;\n                }\n\n                // add other remaining elements\n                while (i < target.size())\n                {\n                    result.push_back(\n                    {\n                        {\"op\", \"add\"},\n                        {\"path\", detail::concat(path, \"/-\")},\n                        {\"value\", target[i]}\n                    });\n                    ++i;\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                // first pass: traverse this object's elements\n                for (auto it = source.cbegin(); it != source.cend(); ++it)\n                {\n                    // escape the key name to be used in a JSON patch\n                    const auto path_key = detail::concat(path, '/', detail::escape(it.key()));\n\n                    if (target.find(it.key()) != target.end())\n                    {\n                        // recursive call to compare object values at key it\n                        auto temp_diff = diff(it.value(), target[it.key()], path_key);\n                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                    }\n                    else\n                    {\n                        // found a key that is not in o -> remove it\n                        result.push_back(object(\n                        {\n                            {\"op\", \"remove\"}, {\"path\", path_key}\n                        }));\n                    }\n                }\n\n                // second pass: traverse other object's elements\n                for (auto it = target.cbegin(); it != target.cend(); ++it)\n                {\n                    if (source.find(it.key()) == source.end())\n                    {\n                        // found a key that is not in this -> add it\n                        const auto path_key = detail::concat(path, '/', detail::escape(it.key()));\n                        result.push_back(\n                        {\n                            {\"op\", \"add\"}, {\"path\", path_key},\n                            {\"value\", it.value()}\n                        });\n                    }\n                }\n\n                break;\n            }\n\n            case value_t::null:\n            case value_t::string:\n            case value_t::boolean:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::number_float:\n            case value_t::binary:\n            case value_t::discarded:\n            default:\n            {\n                // both primitive type: replace value\n                result.push_back(\n                {\n                    {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n                });\n                break;\n            }\n        }\n\n        return result;\n    }\n    /// @}\n\n    ////////////////////////////////\n    // JSON Merge Patch functions //\n    ////////////////////////////////\n\n    /// @name JSON Merge Patch functions\n    /// @{\n\n    /// @brief applies a JSON Merge Patch\n    /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/\n    void merge_patch(const basic_json& apply_patch)\n    {\n        if (apply_patch.is_object())\n        {\n            if (!is_object())\n            {\n                *this = object();\n            }\n            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)\n            {\n                if (it.value().is_null())\n                {\n                    erase(it.key());\n                }\n                else\n                {\n                    operator[](it.key()).merge_patch(it.value());\n                }\n            }\n        }\n        else\n        {\n            *this = apply_patch;\n        }\n    }\n\n    /// @}\n};\n\n/// @brief user-defined to_string function for JSON values\n/// @sa https://json.nlohmann.me/api/basic_json/to_string/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstd::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)\n{\n    return j.dump();\n}\n\ninline namespace literals\n{\ninline namespace json_literals\n{\n\n/// @brief user-defined string literal for JSON values\n/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json operator \"\" _json(const char* s, std::size_t n)\n{\n    return nlohmann::json::parse(s, s + n);\n}\n\n/// @brief user-defined string literal for JSON pointer\n/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/\nJSON_HEDLEY_NON_NULL(1)\ninline nlohmann::json::json_pointer operator \"\" _json_pointer(const char* s, std::size_t n)\n{\n    return nlohmann::json::json_pointer(std::string(s, n));\n}\n\n}  // namespace json_literals\n}  // namespace literals\nNLOHMANN_JSON_NAMESPACE_END\n\n///////////////////////\n// nonmember support //\n///////////////////////\n\nnamespace std // NOLINT(cert-dcl58-cpp)\n{\n\n/// @brief hash value for JSON objects\n/// @sa https://json.nlohmann.me/api/basic_json/std_hash/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstruct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL>\n{\n    std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const\n    {\n        return nlohmann::detail::hash(j);\n    }\n};\n\n// specialization for std::less<value_t>\ntemplate<>\nstruct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679\n{\n    /*!\n    @brief compare two value_t enum values\n    @since version 3.0.0\n    */\n    bool operator()(::nlohmann::detail::value_t lhs,\n                    ::nlohmann::detail::value_t rhs) const noexcept\n    {\n#if JSON_HAS_THREE_WAY_COMPARISON\n        return std::is_lt(lhs <=> rhs); // *NOPAD*\n#else\n        return ::nlohmann::detail::operator<(lhs, rhs);\n#endif\n    }\n};\n\n// C++20 prohibit function specialization in the std namespace.\n#ifndef JSON_HAS_CPP_20\n\n/// @brief exchanges the values of two JSON objects\n/// @sa https://json.nlohmann.me/api/basic_json/std_swap/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\ninline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept(  // NOLINT(readability-inconsistent-declaration-parameter-name)\n    is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&&                          // NOLINT(misc-redundant-expression)\n    is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value)\n{\n    j1.swap(j2);\n}\n\n#endif\n\n}  // namespace std\n\n#if JSON_USE_GLOBAL_UDLS\n    using nlohmann::literals::json_literals::operator \"\" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)\n    using nlohmann::literals::json_literals::operator \"\" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)\n#endif\n\n// #include <nlohmann/detail/macro_unscope.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n// restore clang diagnostic settings\n#if defined(__clang__)\n    #pragma clang diagnostic pop\n#endif\n\n// clean up\n#undef JSON_ASSERT\n#undef JSON_INTERNAL_CATCH\n#undef JSON_THROW\n#undef JSON_PRIVATE_UNLESS_TESTED\n#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION\n#undef NLOHMANN_BASIC_JSON_TPL\n#undef JSON_EXPLICIT\n#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL\n#undef JSON_INLINE_VARIABLE\n#undef JSON_NO_UNIQUE_ADDRESS\n#undef JSON_DISABLE_ENUM_SERIALIZATION\n#undef JSON_USE_GLOBAL_UDLS\n\n#ifndef JSON_TEST_KEEP_MACROS\n    #undef JSON_CATCH\n    #undef JSON_TRY\n    #undef JSON_HAS_CPP_11\n    #undef JSON_HAS_CPP_14\n    #undef JSON_HAS_CPP_17\n    #undef JSON_HAS_CPP_20\n    #undef JSON_HAS_FILESYSTEM\n    #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM\n    #undef JSON_HAS_THREE_WAY_COMPARISON\n    #undef JSON_HAS_RANGES\n    #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON\n#endif\n\n// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>\n//     __ _____ _____ _____\n//  __|  |   __|     |   | |  JSON for Modern C++\n// |  |  |__   |  |  | | | |  version 3.11.2\n// |_____|_____|_____|_|___|  https://github.com/nlohmann/json\n//\n// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>\n// SPDX-License-Identifier: MIT\n\n\n\n#undef JSON_HEDLEY_ALWAYS_INLINE\n#undef JSON_HEDLEY_ARM_VERSION\n#undef JSON_HEDLEY_ARM_VERSION_CHECK\n#undef JSON_HEDLEY_ARRAY_PARAM\n#undef JSON_HEDLEY_ASSUME\n#undef JSON_HEDLEY_BEGIN_C_DECLS\n#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_BUILTIN\n#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_CLANG_HAS_EXTENSION\n#undef JSON_HEDLEY_CLANG_HAS_FEATURE\n#undef JSON_HEDLEY_CLANG_HAS_WARNING\n#undef JSON_HEDLEY_COMPCERT_VERSION\n#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK\n#undef JSON_HEDLEY_CONCAT\n#undef JSON_HEDLEY_CONCAT3\n#undef JSON_HEDLEY_CONCAT3_EX\n#undef JSON_HEDLEY_CONCAT_EX\n#undef JSON_HEDLEY_CONST\n#undef JSON_HEDLEY_CONSTEXPR\n#undef JSON_HEDLEY_CONST_CAST\n#undef JSON_HEDLEY_CPP_CAST\n#undef JSON_HEDLEY_CRAY_VERSION\n#undef JSON_HEDLEY_CRAY_VERSION_CHECK\n#undef JSON_HEDLEY_C_DECL\n#undef JSON_HEDLEY_DEPRECATED\n#undef JSON_HEDLEY_DEPRECATED_FOR\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS\n#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION\n#undef JSON_HEDLEY_DIAGNOSTIC_POP\n#undef JSON_HEDLEY_DIAGNOSTIC_PUSH\n#undef JSON_HEDLEY_DMC_VERSION\n#undef JSON_HEDLEY_DMC_VERSION_CHECK\n#undef JSON_HEDLEY_EMPTY_BASES\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION\n#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK\n#undef JSON_HEDLEY_END_C_DECLS\n#undef JSON_HEDLEY_FLAGS\n#undef JSON_HEDLEY_FLAGS_CAST\n#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_BUILTIN\n#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GCC_HAS_EXTENSION\n#undef JSON_HEDLEY_GCC_HAS_FEATURE\n#undef JSON_HEDLEY_GCC_HAS_WARNING\n#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK\n#undef JSON_HEDLEY_GCC_VERSION\n#undef JSON_HEDLEY_GCC_VERSION_CHECK\n#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_BUILTIN\n#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_GNUC_HAS_EXTENSION\n#undef JSON_HEDLEY_GNUC_HAS_FEATURE\n#undef JSON_HEDLEY_GNUC_HAS_WARNING\n#undef JSON_HEDLEY_GNUC_VERSION\n#undef JSON_HEDLEY_GNUC_VERSION_CHECK\n#undef JSON_HEDLEY_HAS_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_BUILTIN\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS\n#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE\n#undef JSON_HEDLEY_HAS_EXTENSION\n#undef JSON_HEDLEY_HAS_FEATURE\n#undef JSON_HEDLEY_HAS_WARNING\n#undef JSON_HEDLEY_IAR_VERSION\n#undef JSON_HEDLEY_IAR_VERSION_CHECK\n#undef JSON_HEDLEY_IBM_VERSION\n#undef JSON_HEDLEY_IBM_VERSION_CHECK\n#undef JSON_HEDLEY_IMPORT\n#undef JSON_HEDLEY_INLINE\n#undef JSON_HEDLEY_INTEL_CL_VERSION\n#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK\n#undef JSON_HEDLEY_INTEL_VERSION\n#undef JSON_HEDLEY_INTEL_VERSION_CHECK\n#undef JSON_HEDLEY_IS_CONSTANT\n#undef JSON_HEDLEY_IS_CONSTEXPR_\n#undef JSON_HEDLEY_LIKELY\n#undef JSON_HEDLEY_MALLOC\n#undef JSON_HEDLEY_MCST_LCC_VERSION\n#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK\n#undef JSON_HEDLEY_MESSAGE\n#undef JSON_HEDLEY_MSVC_VERSION\n#undef JSON_HEDLEY_MSVC_VERSION_CHECK\n#undef JSON_HEDLEY_NEVER_INLINE\n#undef JSON_HEDLEY_NON_NULL\n#undef JSON_HEDLEY_NO_ESCAPE\n#undef JSON_HEDLEY_NO_RETURN\n#undef JSON_HEDLEY_NO_THROW\n#undef JSON_HEDLEY_NULL\n#undef JSON_HEDLEY_PELLES_VERSION\n#undef JSON_HEDLEY_PELLES_VERSION_CHECK\n#undef JSON_HEDLEY_PGI_VERSION\n#undef JSON_HEDLEY_PGI_VERSION_CHECK\n#undef JSON_HEDLEY_PREDICT\n#undef JSON_HEDLEY_PRINTF_FORMAT\n#undef JSON_HEDLEY_PRIVATE\n#undef JSON_HEDLEY_PUBLIC\n#undef JSON_HEDLEY_PURE\n#undef JSON_HEDLEY_REINTERPRET_CAST\n#undef JSON_HEDLEY_REQUIRE\n#undef JSON_HEDLEY_REQUIRE_CONSTEXPR\n#undef JSON_HEDLEY_REQUIRE_MSG\n#undef JSON_HEDLEY_RESTRICT\n#undef JSON_HEDLEY_RETURNS_NON_NULL\n#undef JSON_HEDLEY_SENTINEL\n#undef JSON_HEDLEY_STATIC_ASSERT\n#undef JSON_HEDLEY_STATIC_CAST\n#undef JSON_HEDLEY_STRINGIFY\n#undef JSON_HEDLEY_STRINGIFY_EX\n#undef JSON_HEDLEY_SUNPRO_VERSION\n#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK\n#undef JSON_HEDLEY_TINYC_VERSION\n#undef JSON_HEDLEY_TINYC_VERSION_CHECK\n#undef JSON_HEDLEY_TI_ARMCL_VERSION\n#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL2000_VERSION\n#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL430_VERSION\n#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL6X_VERSION\n#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CL7X_VERSION\n#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK\n#undef JSON_HEDLEY_TI_CLPRU_VERSION\n#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK\n#undef JSON_HEDLEY_TI_VERSION\n#undef JSON_HEDLEY_TI_VERSION_CHECK\n#undef JSON_HEDLEY_UNAVAILABLE\n#undef JSON_HEDLEY_UNLIKELY\n#undef JSON_HEDLEY_UNPREDICTABLE\n#undef JSON_HEDLEY_UNREACHABLE\n#undef JSON_HEDLEY_UNREACHABLE_RETURN\n#undef JSON_HEDLEY_VERSION\n#undef JSON_HEDLEY_VERSION_DECODE_MAJOR\n#undef JSON_HEDLEY_VERSION_DECODE_MINOR\n#undef JSON_HEDLEY_VERSION_DECODE_REVISION\n#undef JSON_HEDLEY_VERSION_ENCODE\n#undef JSON_HEDLEY_WARNING\n#undef JSON_HEDLEY_WARN_UNUSED_RESULT\n#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG\n#undef JSON_HEDLEY_FALL_THROUGH\n\n\n\n#endif  // INCLUDE_NLOHMANN_JSON_HPP_\n"
  },
  {
    "path": "third-party/l4z/LICENSE",
    "content": "This repository uses 2 different licenses :\n- all files in the `lib` directory use a BSD 2-Clause license\n- all other files use a GPLv2 license, unless explicitly stated otherwise\n\nRelevant license is reminded at the top of each source file,\nand with presence of COPYING or LICENSE file in associated directories.\n\nThis model is selected to emphasize that\nfiles in the `lib` directory are designed to be included into 3rd party applications,\nwhile all other files, in `programs`, `tests` or `examples`,\nare intended to be used \"as is\", as part of their intended scenarios,\nwith no intention to support 3rd party integration use cases.\n"
  },
  {
    "path": "third-party/l4z/liblz4.pc",
    "content": "#   LZ4 - Fast LZ compression algorithm\n#   Copyright (C) 2011-2020, Yann Collet.\n#   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\n\nprefix=/usr/local\nlibdir=${prefix}/lib\nincludedir=${prefix}/include\n\nName: lz4\nDescription: extremely fast lossless compression algorithm library\nURL: http://www.lz4.org/\nVersion: 1.9.4\nLibs: -L${libdir} -llz4\nCflags: -I${includedir}\n"
  },
  {
    "path": "third-party/l4z/liblz4.pc.in",
    "content": "#   LZ4 - Fast LZ compression algorithm\n#   Copyright (C) 2011-2020, Yann Collet.\n#   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\n\nprefix=@PREFIX@\nlibdir=@LIBDIR@\nincludedir=@INCLUDEDIR@\n\nName: lz4\nDescription: extremely fast lossless compression algorithm library\nURL: http://www.lz4.org/\nVersion: @VERSION@\nLibs: -L${libdir} -llz4\nCflags: -I${includedir}\n"
  },
  {
    "path": "third-party/l4z/lz4.c",
    "content": "/*\n   LZ4 - Fast LZ compression algorithm\n   Copyright (C) 2011-2020, Yann Collet.\n\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\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\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   You can contact the author at :\n    - LZ4 homepage : http://www.lz4.org\n    - LZ4 source repository : https://github.com/lz4/lz4\n*/\n\n/*-************************************\n*  Tuning parameters\n**************************************/\n/*\n * LZ4_HEAPMODE :\n * Select how default compression functions will allocate memory for their hash table,\n * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).\n */\n#ifndef LZ4_HEAPMODE\n#  define LZ4_HEAPMODE 0\n#endif\n\n/*\n * LZ4_ACCELERATION_DEFAULT :\n * Select \"acceleration\" for LZ4_compress_fast() when parameter value <= 0\n */\n#define LZ4_ACCELERATION_DEFAULT 1\n/*\n * LZ4_ACCELERATION_MAX :\n * Any \"acceleration\" value higher than this threshold\n * get treated as LZ4_ACCELERATION_MAX instead (fix #876)\n */\n#define LZ4_ACCELERATION_MAX 65537\n\n\n/*-************************************\n*  CPU Feature Detection\n**************************************/\n/* LZ4_FORCE_MEMORY_ACCESS\n * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.\n * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.\n * The below switch allow to select different access method for improved performance.\n * Method 0 (default) : use `memcpy()`. Safe and portable.\n * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).\n *            This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.\n * Method 2 : direct access. This method is portable but violate C standard.\n *            It can generate buggy code on targets which assembly generation depends on alignment.\n *            But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)\n * See https://fastcompression.blogspot.fr/2015/08/accessing-unaligned-memory.html for details.\n * Prefer these methods in priority order (0 > 1 > 2)\n */\n#ifndef LZ4_FORCE_MEMORY_ACCESS   /* can be defined externally */\n#  if defined(__GNUC__) && \\\n  ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) \\\n  || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )\n#    define LZ4_FORCE_MEMORY_ACCESS 2\n#  elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || defined(__GNUC__)\n#    define LZ4_FORCE_MEMORY_ACCESS 1\n#  endif\n#endif\n\n/*\n * LZ4_FORCE_SW_BITCOUNT\n * Define this parameter if your target system or compiler does not support hardware bit count\n */\n#if defined(_MSC_VER) && defined(_WIN32_WCE)   /* Visual Studio for WinCE doesn't support Hardware bit count */\n#  undef  LZ4_FORCE_SW_BITCOUNT  /* avoid double def */\n#  define LZ4_FORCE_SW_BITCOUNT\n#endif\n\n\n\n/*-************************************\n*  Dependency\n**************************************/\n/*\n * LZ4_SRC_INCLUDED:\n * Amalgamation flag, whether lz4.c is included\n */\n#ifndef LZ4_SRC_INCLUDED\n#  define LZ4_SRC_INCLUDED 1\n#endif\n\n#ifndef LZ4_STATIC_LINKING_ONLY\n#define LZ4_STATIC_LINKING_ONLY\n#endif\n\n#ifndef LZ4_DISABLE_DEPRECATE_WARNINGS\n#define LZ4_DISABLE_DEPRECATE_WARNINGS /* due to LZ4_decompress_safe_withPrefix64k */\n#endif\n\n#define LZ4_STATIC_LINKING_ONLY  /* LZ4_DISTANCE_MAX */\n#include \"lz4.h\"\n/* see also \"memory routines\" below */\n\n\n/*-************************************\n*  Compiler Options\n**************************************/\n#if defined(_MSC_VER) && (_MSC_VER >= 1400)  /* Visual Studio 2005+ */\n#  include <intrin.h>               /* only present in VS2005+ */\n#  pragma warning(disable : 4127)   /* disable: C4127: conditional expression is constant */\n#  pragma warning(disable : 6237)   /* disable: C6237: conditional expression is always 0 */\n#endif  /* _MSC_VER */\n\n#ifndef LZ4_FORCE_INLINE\n#  ifdef _MSC_VER    /* Visual Studio */\n#    define LZ4_FORCE_INLINE static __forceinline\n#  else\n#    if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */\n#      ifdef __GNUC__\n#        define LZ4_FORCE_INLINE static inline __attribute__((always_inline))\n#      else\n#        define LZ4_FORCE_INLINE static inline\n#      endif\n#    else\n#      define LZ4_FORCE_INLINE static\n#    endif /* __STDC_VERSION__ */\n#  endif  /* _MSC_VER */\n#endif /* LZ4_FORCE_INLINE */\n\n/* LZ4_FORCE_O2 and LZ4_FORCE_INLINE\n * gcc on ppc64le generates an unrolled SIMDized loop for LZ4_wildCopy8,\n * together with a simple 8-byte copy loop as a fall-back path.\n * However, this optimization hurts the decompression speed by >30%,\n * because the execution does not go to the optimized loop\n * for typical compressible data, and all of the preamble checks\n * before going to the fall-back path become useless overhead.\n * This optimization happens only with the -O3 flag, and -O2 generates\n * a simple 8-byte copy loop.\n * With gcc on ppc64le, all of the LZ4_decompress_* and LZ4_wildCopy8\n * functions are annotated with __attribute__((optimize(\"O2\"))),\n * and also LZ4_wildCopy8 is forcibly inlined, so that the O2 attribute\n * of LZ4_wildCopy8 does not affect the compression speed.\n */\n#if defined(__PPC64__) && defined(__LITTLE_ENDIAN__) && defined(__GNUC__) && !defined(__clang__)\n#  define LZ4_FORCE_O2  __attribute__((optimize(\"O2\")))\n#  undef LZ4_FORCE_INLINE\n#  define LZ4_FORCE_INLINE  static __inline __attribute__((optimize(\"O2\"),always_inline))\n#else\n#  define LZ4_FORCE_O2\n#endif\n\n#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)\n#  define expect(expr,value)    (__builtin_expect ((expr),(value)) )\n#else\n#  define expect(expr,value)    (expr)\n#endif\n\n#ifndef likely\n#define likely(expr)     expect((expr) != 0, 1)\n#endif\n#ifndef unlikely\n#define unlikely(expr)   expect((expr) != 0, 0)\n#endif\n\n/* Should the alignment test prove unreliable, for some reason,\n * it can be disabled by setting LZ4_ALIGN_TEST to 0 */\n#ifndef LZ4_ALIGN_TEST  /* can be externally provided */\n# define LZ4_ALIGN_TEST 1\n#endif\n\n\n/*-************************************\n*  Memory routines\n**************************************/\n\n/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION :\n *  Disable relatively high-level LZ4/HC functions that use dynamic memory\n *  allocation functions (malloc(), calloc(), free()).\n *\n *  Note that this is a compile-time switch. And since it disables\n *  public/stable LZ4 v1 API functions, we don't recommend using this\n *  symbol to generate a library for distribution.\n *\n *  The following public functions are removed when this symbol is defined.\n *  - lz4   : LZ4_createStream, LZ4_freeStream,\n *            LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated)\n *  - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC,\n *            LZ4_createHC (deprecated), LZ4_freeHC  (deprecated)\n *  - lz4frame, lz4file : All LZ4F_* functions\n */\n#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\n#  define ALLOC(s)          lz4_error_memory_allocation_is_disabled\n#  define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled\n#  define FREEMEM(p)        lz4_error_memory_allocation_is_disabled\n#elif defined(LZ4_USER_MEMORY_FUNCTIONS)\n/* memory management functions can be customized by user project.\n * Below functions must exist somewhere in the Project\n * and be available at link time */\nvoid* LZ4_malloc(size_t s);\nvoid* LZ4_calloc(size_t n, size_t s);\nvoid  LZ4_free(void* p);\n# define ALLOC(s)          LZ4_malloc(s)\n# define ALLOC_AND_ZERO(s) LZ4_calloc(1,s)\n# define FREEMEM(p)        LZ4_free(p)\n#else\n# include <stdlib.h>   /* malloc, calloc, free */\n# define ALLOC(s)          malloc(s)\n# define ALLOC_AND_ZERO(s) calloc(1,s)\n# define FREEMEM(p)        free(p)\n#endif\n\n#if ! LZ4_FREESTANDING\n#  include <string.h>   /* memset, memcpy */\n#endif\n#if !defined(LZ4_memset)\n#  define LZ4_memset(p,v,s) memset((p),(v),(s))\n#endif\n#define MEM_INIT(p,v,s)   LZ4_memset((p),(v),(s))\n\n\n/*-************************************\n*  Common Constants\n**************************************/\n#define MINMATCH 4\n\n#define WILDCOPYLENGTH 8\n#define LASTLITERALS   5   /* see ../doc/lz4_Block_format.md#parsing-restrictions */\n#define MFLIMIT       12   /* see ../doc/lz4_Block_format.md#parsing-restrictions */\n#define MATCH_SAFEGUARD_DISTANCE  ((2*WILDCOPYLENGTH) - MINMATCH)   /* ensure it's possible to write 2 x wildcopyLength without overflowing output buffer */\n#define FASTLOOP_SAFE_DISTANCE 64\nstatic const int LZ4_minLength = (MFLIMIT+1);\n\n#define KB *(1 <<10)\n#define MB *(1 <<20)\n#define GB *(1U<<30)\n\n#define LZ4_DISTANCE_ABSOLUTE_MAX 65535\n#if (LZ4_DISTANCE_MAX > LZ4_DISTANCE_ABSOLUTE_MAX)   /* max supported by LZ4 format */\n#  error \"LZ4_DISTANCE_MAX is too big : must be <= 65535\"\n#endif\n\n#define ML_BITS  4\n#define ML_MASK  ((1U<<ML_BITS)-1)\n#define RUN_BITS (8-ML_BITS)\n#define RUN_MASK ((1U<<RUN_BITS)-1)\n\n\n/*-************************************\n*  Error detection\n**************************************/\n#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)\n#  include <assert.h>\n#else\n#  ifndef assert\n#    define assert(condition) ((void)0)\n#  endif\n#endif\n\n#define LZ4_STATIC_ASSERT(c)   { enum { LZ4_static_assert = 1/(int)(!!(c)) }; }   /* use after variable declarations */\n\n#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2)\n#  include <stdio.h>\n   static int g_debuglog_enable = 1;\n#  define DEBUGLOG(l, ...) {                          \\\n        if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) {  \\\n            fprintf(stderr, __FILE__ \": \");           \\\n            fprintf(stderr, __VA_ARGS__);             \\\n            fprintf(stderr, \" \\n\");                   \\\n    }   }\n#else\n#  define DEBUGLOG(l, ...) {}    /* disabled */\n#endif\n\nstatic int LZ4_isAligned(const void* ptr, size_t alignment)\n{\n    return ((size_t)ptr & (alignment -1)) == 0;\n}\n\n\n/*-************************************\n*  Types\n**************************************/\n#include <limits.h>\n#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)\n# include <stdint.h>\n  typedef  uint8_t BYTE;\n  typedef uint16_t U16;\n  typedef uint32_t U32;\n  typedef  int32_t S32;\n  typedef uint64_t U64;\n  typedef uintptr_t uptrval;\n#else\n# if UINT_MAX != 4294967295UL\n#   error \"LZ4 code (when not C++ or C99) assumes that sizeof(int) == 4\"\n# endif\n  typedef unsigned char       BYTE;\n  typedef unsigned short      U16;\n  typedef unsigned int        U32;\n  typedef   signed int        S32;\n  typedef unsigned long long  U64;\n  typedef size_t              uptrval;   /* generally true, except OpenVMS-64 */\n#endif\n\n#if defined(__x86_64__)\n  typedef U64    reg_t;   /* 64-bits in x32 mode */\n#else\n  typedef size_t reg_t;   /* 32-bits in x32 mode */\n#endif\n\ntypedef enum {\n    notLimited = 0,\n    limitedOutput = 1,\n    fillOutput = 2\n} limitedOutput_directive;\n\n\n/*-************************************\n*  Reading and writing into memory\n**************************************/\n\n/**\n * LZ4 relies on memcpy with a constant size being inlined. In freestanding\n * environments, the compiler can't assume the implementation of memcpy() is\n * standard compliant, so it can't apply its specialized memcpy() inlining\n * logic. When possible, use __builtin_memcpy() to tell the compiler to analyze\n * memcpy() as if it were standard compliant, so it can inline it in freestanding\n * environments. This is needed when decompressing the Linux Kernel, for example.\n */\n#if !defined(LZ4_memcpy)\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size)\n#  else\n#    define LZ4_memcpy(dst, src, size) memcpy(dst, src, size)\n#  endif\n#endif\n\n#if !defined(LZ4_memmove)\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define LZ4_memmove __builtin_memmove\n#  else\n#    define LZ4_memmove memmove\n#  endif\n#endif\n\nstatic unsigned LZ4_isLittleEndian(void)\n{\n    const union { U32 u; BYTE c[4]; } one = { 1 };   /* don't use static : performance detrimental */\n    return one.c[0];\n}\n\n\n#if defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==2)\n/* lie to the compiler about data alignment; use with caution */\n\nstatic U16 LZ4_read16(const void* memPtr) { return *(const U16*) memPtr; }\nstatic U32 LZ4_read32(const void* memPtr) { return *(const U32*) memPtr; }\nstatic reg_t LZ4_read_ARCH(const void* memPtr) { return *(const reg_t*) memPtr; }\n\nstatic void LZ4_write16(void* memPtr, U16 value) { *(U16*)memPtr = value; }\nstatic void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; }\n\n#elif defined(LZ4_FORCE_MEMORY_ACCESS) && (LZ4_FORCE_MEMORY_ACCESS==1)\n\n/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */\n/* currently only defined for gcc and icc */\ntypedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) LZ4_unalign;\n\nstatic U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign*)ptr)->u16; }\nstatic U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign*)ptr)->u32; }\nstatic reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalign*)ptr)->uArch; }\n\nstatic void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign*)memPtr)->u16 = value; }\nstatic void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign*)memPtr)->u32 = value; }\n\n#else  /* safe and portable access using memcpy() */\n\nstatic U16 LZ4_read16(const void* memPtr)\n{\n    U16 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic U32 LZ4_read32(const void* memPtr)\n{\n    U32 val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic reg_t LZ4_read_ARCH(const void* memPtr)\n{\n    reg_t val; LZ4_memcpy(&val, memPtr, sizeof(val)); return val;\n}\n\nstatic void LZ4_write16(void* memPtr, U16 value)\n{\n    LZ4_memcpy(memPtr, &value, sizeof(value));\n}\n\nstatic void LZ4_write32(void* memPtr, U32 value)\n{\n    LZ4_memcpy(memPtr, &value, sizeof(value));\n}\n\n#endif /* LZ4_FORCE_MEMORY_ACCESS */\n\n\nstatic U16 LZ4_readLE16(const void* memPtr)\n{\n    if (LZ4_isLittleEndian()) {\n        return LZ4_read16(memPtr);\n    } else {\n        const BYTE* p = (const BYTE*)memPtr;\n        return (U16)((U16)p[0] + (p[1]<<8));\n    }\n}\n\nstatic void LZ4_writeLE16(void* memPtr, U16 value)\n{\n    if (LZ4_isLittleEndian()) {\n        LZ4_write16(memPtr, value);\n    } else {\n        BYTE* p = (BYTE*)memPtr;\n        p[0] = (BYTE) value;\n        p[1] = (BYTE)(value>>8);\n    }\n}\n\n/* customized variant of memcpy, which can overwrite up to 8 bytes beyond dstEnd */\nLZ4_FORCE_INLINE\nvoid LZ4_wildCopy8(void* dstPtr, const void* srcPtr, void* dstEnd)\n{\n    BYTE* d = (BYTE*)dstPtr;\n    const BYTE* s = (const BYTE*)srcPtr;\n    BYTE* const e = (BYTE*)dstEnd;\n\n    do { LZ4_memcpy(d,s,8); d+=8; s+=8; } while (d<e);\n}\n\nstatic const unsigned inc32table[8] = {0, 1, 2,  1,  0,  4, 4, 4};\nstatic const int      dec64table[8] = {0, 0, 0, -1, -4,  1, 2, 3};\n\n\n#ifndef LZ4_FAST_DEC_LOOP\n#  if defined __i386__ || defined _M_IX86 || defined __x86_64__ || defined _M_X64\n#    define LZ4_FAST_DEC_LOOP 1\n#  elif defined(__aarch64__) && defined(__APPLE__)\n#    define LZ4_FAST_DEC_LOOP 1\n#  elif defined(__aarch64__) && !defined(__clang__)\n     /* On non-Apple aarch64, we disable this optimization for clang because\n      * on certain mobile chipsets, performance is reduced with clang. For\n      * more information refer to https://github.com/lz4/lz4/pull/707 */\n#    define LZ4_FAST_DEC_LOOP 1\n#  else\n#    define LZ4_FAST_DEC_LOOP 0\n#  endif\n#endif\n\n#if LZ4_FAST_DEC_LOOP\n\nLZ4_FORCE_INLINE void\nLZ4_memcpy_using_offset_base(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)\n{\n    assert(srcPtr + offset == dstPtr);\n    if (offset < 8) {\n        LZ4_write32(dstPtr, 0);   /* silence an msan warning when offset==0 */\n        dstPtr[0] = srcPtr[0];\n        dstPtr[1] = srcPtr[1];\n        dstPtr[2] = srcPtr[2];\n        dstPtr[3] = srcPtr[3];\n        srcPtr += inc32table[offset];\n        LZ4_memcpy(dstPtr+4, srcPtr, 4);\n        srcPtr -= dec64table[offset];\n        dstPtr += 8;\n    } else {\n        LZ4_memcpy(dstPtr, srcPtr, 8);\n        dstPtr += 8;\n        srcPtr += 8;\n    }\n\n    LZ4_wildCopy8(dstPtr, srcPtr, dstEnd);\n}\n\n/* customized variant of memcpy, which can overwrite up to 32 bytes beyond dstEnd\n * this version copies two times 16 bytes (instead of one time 32 bytes)\n * because it must be compatible with offsets >= 16. */\nLZ4_FORCE_INLINE void\nLZ4_wildCopy32(void* dstPtr, const void* srcPtr, void* dstEnd)\n{\n    BYTE* d = (BYTE*)dstPtr;\n    const BYTE* s = (const BYTE*)srcPtr;\n    BYTE* const e = (BYTE*)dstEnd;\n\n    do { LZ4_memcpy(d,s,16); LZ4_memcpy(d+16,s+16,16); d+=32; s+=32; } while (d<e);\n}\n\n/* LZ4_memcpy_using_offset()  presumes :\n * - dstEnd >= dstPtr + MINMATCH\n * - there is at least 8 bytes available to write after dstEnd */\nLZ4_FORCE_INLINE void\nLZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const size_t offset)\n{\n    BYTE v[8];\n\n    assert(dstEnd >= dstPtr + MINMATCH);\n\n    switch(offset) {\n    case 1:\n        MEM_INIT(v, *srcPtr, 8);\n        break;\n    case 2:\n        LZ4_memcpy(v, srcPtr, 2);\n        LZ4_memcpy(&v[2], srcPtr, 2);\n#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */\n#  pragma warning(push)\n#  pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */\n#endif\n        LZ4_memcpy(&v[4], v, 4);\n#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */\n#  pragma warning(pop)\n#endif\n        break;\n    case 4:\n        LZ4_memcpy(v, srcPtr, 4);\n        LZ4_memcpy(&v[4], srcPtr, 4);\n        break;\n    default:\n        LZ4_memcpy_using_offset_base(dstPtr, srcPtr, dstEnd, offset);\n        return;\n    }\n\n    LZ4_memcpy(dstPtr, v, 8);\n    dstPtr += 8;\n    while (dstPtr < dstEnd) {\n        LZ4_memcpy(dstPtr, v, 8);\n        dstPtr += 8;\n    }\n}\n#endif\n\n\n/*-************************************\n*  Common functions\n**************************************/\nstatic unsigned LZ4_NbCommonBytes (reg_t val)\n{\n    assert(val != 0);\n    if (LZ4_isLittleEndian()) {\n        if (sizeof(val) == 8) {\n#       if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT)\n/*-*************************************************************************************************\n* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11.\n* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics\n* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC.\n****************************************************************************************************/\n#         if defined(__clang__) && (__clang_major__ < 10)\n            /* Avoid undefined clang-cl intrinsics issue.\n             * See https://github.com/lz4/lz4/pull/1017 for details. */\n            return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3;\n#         else\n            /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */\n            return (unsigned)_tzcnt_u64(val) >> 3;\n#         endif\n#       elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            unsigned long r = 0;\n            _BitScanForward64(&r, (U64)val);\n            return (unsigned)r >> 3;\n#       elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                                        !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_ctzll((U64)val) >> 3;\n#       else\n            const U64 m = 0x0101010101010101ULL;\n            val ^= val - 1;\n            return (unsigned)(((U64)((val & (m - 1)) * m)) >> 56);\n#       endif\n        } else /* 32 bits */ {\n#       if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            unsigned long r;\n            _BitScanForward(&r, (U32)val);\n            return (unsigned)r >> 3;\n#       elif (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                        !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_ctz((U32)val) >> 3;\n#       else\n            const U32 m = 0x01010101;\n            return (unsigned)((((val - 1) ^ val) & (m - 1)) * m) >> 24;\n#       endif\n        }\n    } else   /* Big Endian CPU */ {\n        if (sizeof(val)==8) {\n#       if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                        !defined(__TINYC__) && !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_clzll((U64)val) >> 3;\n#       else\n#if 1\n            /* this method is probably faster,\n             * but adds a 128 bytes lookup table */\n            static const unsigned char ctz7_tab[128] = {\n                7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n                4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,\n            };\n            U64 const mask = 0x0101010101010101ULL;\n            U64 const t = (((val >> 8) - mask) | val) & mask;\n            return ctz7_tab[(t * 0x0080402010080402ULL) >> 57];\n#else\n            /* this method doesn't consume memory space like the previous one,\n             * but it contains several branches,\n             * that may end up slowing execution */\n            static const U32 by32 = sizeof(val)*4;  /* 32 on 64 bits (goal), 16 on 32 bits.\n            Just to avoid some static analyzer complaining about shift by 32 on 32-bits target.\n            Note that this code path is never triggered in 32-bits mode. */\n            unsigned r;\n            if (!(val>>by32)) { r=4; } else { r=0; val>>=by32; }\n            if (!(val>>16)) { r+=2; val>>=8; } else { val>>=24; }\n            r += (!val);\n            return r;\n#endif\n#       endif\n        } else /* 32 bits */ {\n#       if (defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 3) || \\\n                            ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 4))))) && \\\n                                        !defined(LZ4_FORCE_SW_BITCOUNT)\n            return (unsigned)__builtin_clz((U32)val) >> 3;\n#       else\n            val >>= 8;\n            val = ((((val + 0x00FFFF00) | 0x00FFFFFF) + val) |\n              (val + 0x00FF0000)) >> 24;\n            return (unsigned)val ^ 3;\n#       endif\n        }\n    }\n}\n\n\n#define STEPSIZE sizeof(reg_t)\nLZ4_FORCE_INLINE\nunsigned LZ4_count(const BYTE* pIn, const BYTE* pMatch, const BYTE* pInLimit)\n{\n    const BYTE* const pStart = pIn;\n\n    if (likely(pIn < pInLimit-(STEPSIZE-1))) {\n        reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);\n        if (!diff) {\n            pIn+=STEPSIZE; pMatch+=STEPSIZE;\n        } else {\n            return LZ4_NbCommonBytes(diff);\n    }   }\n\n    while (likely(pIn < pInLimit-(STEPSIZE-1))) {\n        reg_t const diff = LZ4_read_ARCH(pMatch) ^ LZ4_read_ARCH(pIn);\n        if (!diff) { pIn+=STEPSIZE; pMatch+=STEPSIZE; continue; }\n        pIn += LZ4_NbCommonBytes(diff);\n        return (unsigned)(pIn - pStart);\n    }\n\n    if ((STEPSIZE==8) && (pIn<(pInLimit-3)) && (LZ4_read32(pMatch) == LZ4_read32(pIn))) { pIn+=4; pMatch+=4; }\n    if ((pIn<(pInLimit-1)) && (LZ4_read16(pMatch) == LZ4_read16(pIn))) { pIn+=2; pMatch+=2; }\n    if ((pIn<pInLimit) && (*pMatch == *pIn)) pIn++;\n    return (unsigned)(pIn - pStart);\n}\n\n\n#ifndef LZ4_COMMONDEFS_ONLY\n/*-************************************\n*  Local Constants\n**************************************/\nstatic const int LZ4_64Klimit = ((64 KB) + (MFLIMIT-1));\nstatic const U32 LZ4_skipTrigger = 6;  /* Increase this value ==> compression run slower on incompressible data */\n\n\n/*-************************************\n*  Local Structures and types\n**************************************/\ntypedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t;\n\n/**\n * This enum distinguishes several different modes of accessing previous\n * content in the stream.\n *\n * - noDict        : There is no preceding content.\n * - withPrefix64k : Table entries up to ctx->dictSize before the current blob\n *                   blob being compressed are valid and refer to the preceding\n *                   content (of length ctx->dictSize), which is available\n *                   contiguously preceding in memory the content currently\n *                   being compressed.\n * - usingExtDict  : Like withPrefix64k, but the preceding content is somewhere\n *                   else in memory, starting at ctx->dictionary with length\n *                   ctx->dictSize.\n * - usingDictCtx  : Everything concerning the preceding content is\n *                   in a separate context, pointed to by ctx->dictCtx.\n *                   ctx->dictionary, ctx->dictSize, and table entries\n *                   in the current context that refer to positions\n *                   preceding the beginning of the current compression are\n *                   ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx\n *                   ->dictSize describe the location and size of the preceding\n *                   content, and matches are found by looking in the ctx\n *                   ->dictCtx->hashTable.\n */\ntypedef enum { noDict = 0, withPrefix64k, usingExtDict, usingDictCtx } dict_directive;\ntypedef enum { noDictIssue = 0, dictSmall } dictIssue_directive;\n\n\n/*-************************************\n*  Local Utils\n**************************************/\nint LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; }\nconst char* LZ4_versionString(void) { return LZ4_VERSION_STRING; }\nint LZ4_compressBound(int isize)  { return LZ4_COMPRESSBOUND(isize); }\nint LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); }\n\n\n/*-****************************************\n*  Internal Definitions, used only in Tests\n*******************************************/\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\nint LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize);\n\nint LZ4_decompress_safe_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int maxOutputSize,\n                                     const void* dictStart, size_t dictSize);\nint LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int targetOutputSize, int dstCapacity,\n                                     const void* dictStart, size_t dictSize);\n#if defined (__cplusplus)\n}\n#endif\n\n/*-******************************\n*  Compression functions\n********************************/\nLZ4_FORCE_INLINE U32 LZ4_hash4(U32 sequence, tableType_t const tableType)\n{\n    if (tableType == byU16)\n        return ((sequence * 2654435761U) >> ((MINMATCH*8)-(LZ4_HASHLOG+1)));\n    else\n        return ((sequence * 2654435761U) >> ((MINMATCH*8)-LZ4_HASHLOG));\n}\n\nLZ4_FORCE_INLINE U32 LZ4_hash5(U64 sequence, tableType_t const tableType)\n{\n    const U32 hashLog = (tableType == byU16) ? LZ4_HASHLOG+1 : LZ4_HASHLOG;\n    if (LZ4_isLittleEndian()) {\n        const U64 prime5bytes = 889523592379ULL;\n        return (U32)(((sequence << 24) * prime5bytes) >> (64 - hashLog));\n    } else {\n        const U64 prime8bytes = 11400714785074694791ULL;\n        return (U32)(((sequence >> 24) * prime8bytes) >> (64 - hashLog));\n    }\n}\n\nLZ4_FORCE_INLINE U32 LZ4_hashPosition(const void* const p, tableType_t const tableType)\n{\n    if ((sizeof(reg_t)==8) && (tableType != byU16)) return LZ4_hash5(LZ4_read_ARCH(p), tableType);\n    return LZ4_hash4(LZ4_read32(p), tableType);\n}\n\nLZ4_FORCE_INLINE void LZ4_clearHash(U32 h, void* tableBase, tableType_t const tableType)\n{\n    switch (tableType)\n    {\n    default: /* fallthrough */\n    case clearedTable: { /* illegal! */ assert(0); return; }\n    case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = NULL; return; }\n    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = 0; return; }\n    case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = 0; return; }\n    }\n}\n\nLZ4_FORCE_INLINE void LZ4_putIndexOnHash(U32 idx, U32 h, void* tableBase, tableType_t const tableType)\n{\n    switch (tableType)\n    {\n    default: /* fallthrough */\n    case clearedTable: /* fallthrough */\n    case byPtr: { /* illegal! */ assert(0); return; }\n    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = idx; return; }\n    case byU16: { U16* hashTable = (U16*) tableBase; assert(idx < 65536); hashTable[h] = (U16)idx; return; }\n    }\n}\n\nLZ4_FORCE_INLINE void LZ4_putPositionOnHash(const BYTE* p, U32 h,\n                                  void* tableBase, tableType_t const tableType,\n                            const BYTE* srcBase)\n{\n    switch (tableType)\n    {\n    case clearedTable: { /* illegal! */ assert(0); return; }\n    case byPtr: { const BYTE** hashTable = (const BYTE**)tableBase; hashTable[h] = p; return; }\n    case byU32: { U32* hashTable = (U32*) tableBase; hashTable[h] = (U32)(p-srcBase); return; }\n    case byU16: { U16* hashTable = (U16*) tableBase; hashTable[h] = (U16)(p-srcBase); return; }\n    }\n}\n\nLZ4_FORCE_INLINE void LZ4_putPosition(const BYTE* p, void* tableBase, tableType_t tableType, const BYTE* srcBase)\n{\n    U32 const h = LZ4_hashPosition(p, tableType);\n    LZ4_putPositionOnHash(p, h, tableBase, tableType, srcBase);\n}\n\n/* LZ4_getIndexOnHash() :\n * Index of match position registered in hash table.\n * hash position must be calculated by using base+index, or dictBase+index.\n * Assumption 1 : only valid if tableType == byU32 or byU16.\n * Assumption 2 : h is presumed valid (within limits of hash table)\n */\nLZ4_FORCE_INLINE U32 LZ4_getIndexOnHash(U32 h, const void* tableBase, tableType_t tableType)\n{\n    LZ4_STATIC_ASSERT(LZ4_MEMORY_USAGE > 2);\n    if (tableType == byU32) {\n        const U32* const hashTable = (const U32*) tableBase;\n        assert(h < (1U << (LZ4_MEMORY_USAGE-2)));\n        return hashTable[h];\n    }\n    if (tableType == byU16) {\n        const U16* const hashTable = (const U16*) tableBase;\n        assert(h < (1U << (LZ4_MEMORY_USAGE-1)));\n        return hashTable[h];\n    }\n    assert(0); return 0;  /* forbidden case */\n}\n\nstatic const BYTE* LZ4_getPositionOnHash(U32 h, const void* tableBase, tableType_t tableType, const BYTE* srcBase)\n{\n    if (tableType == byPtr) { const BYTE* const* hashTable = (const BYTE* const*) tableBase; return hashTable[h]; }\n    if (tableType == byU32) { const U32* const hashTable = (const U32*) tableBase; return hashTable[h] + srcBase; }\n    { const U16* const hashTable = (const U16*) tableBase; return hashTable[h] + srcBase; }   /* default, to ensure a return */\n}\n\nLZ4_FORCE_INLINE const BYTE*\nLZ4_getPosition(const BYTE* p,\n                const void* tableBase, tableType_t tableType,\n                const BYTE* srcBase)\n{\n    U32 const h = LZ4_hashPosition(p, tableType);\n    return LZ4_getPositionOnHash(h, tableBase, tableType, srcBase);\n}\n\nLZ4_FORCE_INLINE void\nLZ4_prepareTable(LZ4_stream_t_internal* const cctx,\n           const int inputSize,\n           const tableType_t tableType) {\n    /* If the table hasn't been used, it's guaranteed to be zeroed out, and is\n     * therefore safe to use no matter what mode we're in. Otherwise, we figure\n     * out if it's safe to leave as is or whether it needs to be reset.\n     */\n    if ((tableType_t)cctx->tableType != clearedTable) {\n        assert(inputSize >= 0);\n        if ((tableType_t)cctx->tableType != tableType\n          || ((tableType == byU16) && cctx->currentOffset + (unsigned)inputSize >= 0xFFFFU)\n          || ((tableType == byU32) && cctx->currentOffset > 1 GB)\n          || tableType == byPtr\n          || inputSize >= 4 KB)\n        {\n            DEBUGLOG(4, \"LZ4_prepareTable: Resetting table in %p\", cctx);\n            MEM_INIT(cctx->hashTable, 0, LZ4_HASHTABLESIZE);\n            cctx->currentOffset = 0;\n            cctx->tableType = (U32)clearedTable;\n        } else {\n            DEBUGLOG(4, \"LZ4_prepareTable: Re-use hash table (no reset)\");\n        }\n    }\n\n    /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back,\n     * is faster than compressing without a gap.\n     * However, compressing with currentOffset == 0 is faster still,\n     * so we preserve that case.\n     */\n    if (cctx->currentOffset != 0 && tableType == byU32) {\n        DEBUGLOG(5, \"LZ4_prepareTable: adding 64KB to currentOffset\");\n        cctx->currentOffset += 64 KB;\n    }\n\n    /* Finally, clear history */\n    cctx->dictCtx = NULL;\n    cctx->dictionary = NULL;\n    cctx->dictSize = 0;\n}\n\n/** LZ4_compress_generic() :\n *  inlined, to ensure branches are decided at compilation time.\n *  Presumed already validated at this stage:\n *  - source != NULL\n *  - inputSize > 0\n */\nLZ4_FORCE_INLINE int LZ4_compress_generic_validated(\n                 LZ4_stream_t_internal* const cctx,\n                 const char* const source,\n                 char* const dest,\n                 const int inputSize,\n                 int*  inputConsumed, /* only written when outputDirective == fillOutput */\n                 const int maxOutputSize,\n                 const limitedOutput_directive outputDirective,\n                 const tableType_t tableType,\n                 const dict_directive dictDirective,\n                 const dictIssue_directive dictIssue,\n                 const int acceleration)\n{\n    int result;\n    const BYTE* ip = (const BYTE*) source;\n\n    U32 const startIndex = cctx->currentOffset;\n    const BYTE* base = (const BYTE*) source - startIndex;\n    const BYTE* lowLimit;\n\n    const LZ4_stream_t_internal* dictCtx = (const LZ4_stream_t_internal*) cctx->dictCtx;\n    const BYTE* const dictionary =\n        dictDirective == usingDictCtx ? dictCtx->dictionary : cctx->dictionary;\n    const U32 dictSize =\n        dictDirective == usingDictCtx ? dictCtx->dictSize : cctx->dictSize;\n    const U32 dictDelta = (dictDirective == usingDictCtx) ? startIndex - dictCtx->currentOffset : 0;   /* make indexes in dictCtx comparable with index in current context */\n\n    int const maybe_extMem = (dictDirective == usingExtDict) || (dictDirective == usingDictCtx);\n    U32 const prefixIdxLimit = startIndex - dictSize;   /* used when dictDirective == dictSmall */\n    const BYTE* const dictEnd = dictionary ? dictionary + dictSize : dictionary;\n    const BYTE* anchor = (const BYTE*) source;\n    const BYTE* const iend = ip + inputSize;\n    const BYTE* const mflimitPlusOne = iend - MFLIMIT + 1;\n    const BYTE* const matchlimit = iend - LASTLITERALS;\n\n    /* the dictCtx currentOffset is indexed on the start of the dictionary,\n     * while a dictionary in the current context precedes the currentOffset */\n    const BYTE* dictBase = (dictionary == NULL) ? NULL :\n                           (dictDirective == usingDictCtx) ?\n                            dictionary + dictSize - dictCtx->currentOffset :\n                            dictionary + dictSize - startIndex;\n\n    BYTE* op = (BYTE*) dest;\n    BYTE* const olimit = op + maxOutputSize;\n\n    U32 offset = 0;\n    U32 forwardH;\n\n    DEBUGLOG(5, \"LZ4_compress_generic_validated: srcSize=%i, tableType=%u\", inputSize, tableType);\n    assert(ip != NULL);\n    /* If init conditions are not met, we don't have to mark stream\n     * as having dirty context, since no action was taken yet */\n    if (outputDirective == fillOutput && maxOutputSize < 1) { return 0; } /* Impossible to store anything */\n    if ((tableType == byU16) && (inputSize>=LZ4_64Klimit)) { return 0; }  /* Size too large (not within 64K limit) */\n    if (tableType==byPtr) assert(dictDirective==noDict);      /* only supported use case with byPtr */\n    assert(acceleration >= 1);\n\n    lowLimit = (const BYTE*)source - (dictDirective == withPrefix64k ? dictSize : 0);\n\n    /* Update context state */\n    if (dictDirective == usingDictCtx) {\n        /* Subsequent linked blocks can't use the dictionary. */\n        /* Instead, they use the block we just compressed. */\n        cctx->dictCtx = NULL;\n        cctx->dictSize = (U32)inputSize;\n    } else {\n        cctx->dictSize += (U32)inputSize;\n    }\n    cctx->currentOffset += (U32)inputSize;\n    cctx->tableType = (U32)tableType;\n\n    if (inputSize<LZ4_minLength) goto _last_literals;        /* Input too small, no compression (all literals) */\n\n    /* First Byte */\n    LZ4_putPosition(ip, cctx->hashTable, tableType, base);\n    ip++; forwardH = LZ4_hashPosition(ip, tableType);\n\n    /* Main Loop */\n    for ( ; ; ) {\n        const BYTE* match;\n        BYTE* token;\n        const BYTE* filledIp;\n\n        /* Find a match */\n        if (tableType == byPtr) {\n            const BYTE* forwardIp = ip;\n            int step = 1;\n            int searchMatchNb = acceleration << LZ4_skipTrigger;\n            do {\n                U32 const h = forwardH;\n                ip = forwardIp;\n                forwardIp += step;\n                step = (searchMatchNb++ >> LZ4_skipTrigger);\n\n                if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;\n                assert(ip < mflimitPlusOne);\n\n                match = LZ4_getPositionOnHash(h, cctx->hashTable, tableType, base);\n                forwardH = LZ4_hashPosition(forwardIp, tableType);\n                LZ4_putPositionOnHash(ip, h, cctx->hashTable, tableType, base);\n\n            } while ( (match+LZ4_DISTANCE_MAX < ip)\n                   || (LZ4_read32(match) != LZ4_read32(ip)) );\n\n        } else {   /* byU32, byU16 */\n\n            const BYTE* forwardIp = ip;\n            int step = 1;\n            int searchMatchNb = acceleration << LZ4_skipTrigger;\n            do {\n                U32 const h = forwardH;\n                U32 const current = (U32)(forwardIp - base);\n                U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);\n                assert(matchIndex <= current);\n                assert(forwardIp - base < (ptrdiff_t)(2 GB - 1));\n                ip = forwardIp;\n                forwardIp += step;\n                step = (searchMatchNb++ >> LZ4_skipTrigger);\n\n                if (unlikely(forwardIp > mflimitPlusOne)) goto _last_literals;\n                assert(ip < mflimitPlusOne);\n\n                if (dictDirective == usingDictCtx) {\n                    if (matchIndex < startIndex) {\n                        /* there was no match, try the dictionary */\n                        assert(tableType == byU32);\n                        matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);\n                        match = dictBase + matchIndex;\n                        matchIndex += dictDelta;   /* make dictCtx index comparable with current context */\n                        lowLimit = dictionary;\n                    } else {\n                        match = base + matchIndex;\n                        lowLimit = (const BYTE*)source;\n                    }\n                } else if (dictDirective == usingExtDict) {\n                    if (matchIndex < startIndex) {\n                        DEBUGLOG(7, \"extDict candidate: matchIndex=%5u  <  startIndex=%5u\", matchIndex, startIndex);\n                        assert(startIndex - matchIndex >= MINMATCH);\n                        assert(dictBase);\n                        match = dictBase + matchIndex;\n                        lowLimit = dictionary;\n                    } else {\n                        match = base + matchIndex;\n                        lowLimit = (const BYTE*)source;\n                    }\n                } else {   /* single continuous memory segment */\n                    match = base + matchIndex;\n                }\n                forwardH = LZ4_hashPosition(forwardIp, tableType);\n                LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);\n\n                DEBUGLOG(7, \"candidate at pos=%u  (offset=%u \\n\", matchIndex, current - matchIndex);\n                if ((dictIssue == dictSmall) && (matchIndex < prefixIdxLimit)) { continue; }    /* match outside of valid area */\n                assert(matchIndex < current);\n                if ( ((tableType != byU16) || (LZ4_DISTANCE_MAX < LZ4_DISTANCE_ABSOLUTE_MAX))\n                  && (matchIndex+LZ4_DISTANCE_MAX < current)) {\n                    continue;\n                } /* too far */\n                assert((current - matchIndex) <= LZ4_DISTANCE_MAX);  /* match now expected within distance */\n\n                if (LZ4_read32(match) == LZ4_read32(ip)) {\n                    if (maybe_extMem) offset = current - matchIndex;\n                    break;   /* match found */\n                }\n\n            } while(1);\n        }\n\n        /* Catch up */\n        filledIp = ip;\n        while (((ip>anchor) & (match > lowLimit)) && (unlikely(ip[-1]==match[-1]))) { ip--; match--; }\n\n        /* Encode Literals */\n        {   unsigned const litLength = (unsigned)(ip - anchor);\n            token = op++;\n            if ((outputDirective == limitedOutput) &&  /* Check output buffer overflow */\n                (unlikely(op + litLength + (2 + 1 + LASTLITERALS) + (litLength/255) > olimit)) ) {\n                return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n            }\n            if ((outputDirective == fillOutput) &&\n                (unlikely(op + (litLength+240)/255 /* litlen */ + litLength /* literals */ + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit))) {\n                op--;\n                goto _last_literals;\n            }\n            if (litLength >= RUN_MASK) {\n                int len = (int)(litLength - RUN_MASK);\n                *token = (RUN_MASK<<ML_BITS);\n                for(; len >= 255 ; len-=255) *op++ = 255;\n                *op++ = (BYTE)len;\n            }\n            else *token = (BYTE)(litLength<<ML_BITS);\n\n            /* Copy Literals */\n            LZ4_wildCopy8(op, anchor, op+litLength);\n            op+=litLength;\n            DEBUGLOG(6, \"seq.start:%i, literals=%u, match.start:%i\",\n                        (int)(anchor-(const BYTE*)source), litLength, (int)(ip-(const BYTE*)source));\n        }\n\n_next_match:\n        /* at this stage, the following variables must be correctly set :\n         * - ip : at start of LZ operation\n         * - match : at start of previous pattern occurrence; can be within current prefix, or within extDict\n         * - offset : if maybe_ext_memSegment==1 (constant)\n         * - lowLimit : must be == dictionary to mean \"match is within extDict\"; must be == source otherwise\n         * - token and *token : position to write 4-bits for match length; higher 4-bits for literal length supposed already written\n         */\n\n        if ((outputDirective == fillOutput) &&\n            (op + 2 /* offset */ + 1 /* token */ + MFLIMIT - MINMATCH /* min last literals so last match is <= end - MFLIMIT */ > olimit)) {\n            /* the match was too close to the end, rewind and go to last literals */\n            op = token;\n            goto _last_literals;\n        }\n\n        /* Encode Offset */\n        if (maybe_extMem) {   /* static test */\n            DEBUGLOG(6, \"             with offset=%u  (ext if > %i)\", offset, (int)(ip - (const BYTE*)source));\n            assert(offset <= LZ4_DISTANCE_MAX && offset > 0);\n            LZ4_writeLE16(op, (U16)offset); op+=2;\n        } else  {\n            DEBUGLOG(6, \"             with offset=%u  (same segment)\", (U32)(ip - match));\n            assert(ip-match <= LZ4_DISTANCE_MAX);\n            LZ4_writeLE16(op, (U16)(ip - match)); op+=2;\n        }\n\n        /* Encode MatchLength */\n        {   unsigned matchCode;\n\n            if ( (dictDirective==usingExtDict || dictDirective==usingDictCtx)\n              && (lowLimit==dictionary) /* match within extDict */ ) {\n                const BYTE* limit = ip + (dictEnd-match);\n                assert(dictEnd > match);\n                if (limit > matchlimit) limit = matchlimit;\n                matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, limit);\n                ip += (size_t)matchCode + MINMATCH;\n                if (ip==limit) {\n                    unsigned const more = LZ4_count(limit, (const BYTE*)source, matchlimit);\n                    matchCode += more;\n                    ip += more;\n                }\n                DEBUGLOG(6, \"             with matchLength=%u starting in extDict\", matchCode+MINMATCH);\n            } else {\n                matchCode = LZ4_count(ip+MINMATCH, match+MINMATCH, matchlimit);\n                ip += (size_t)matchCode + MINMATCH;\n                DEBUGLOG(6, \"             with matchLength=%u\", matchCode+MINMATCH);\n            }\n\n            if ((outputDirective) &&    /* Check output buffer overflow */\n                (unlikely(op + (1 + LASTLITERALS) + (matchCode+240)/255 > olimit)) ) {\n                if (outputDirective == fillOutput) {\n                    /* Match description too long : reduce it */\n                    U32 newMatchCode = 15 /* in token */ - 1 /* to avoid needing a zero byte */ + ((U32)(olimit - op) - 1 - LASTLITERALS) * 255;\n                    ip -= matchCode - newMatchCode;\n                    assert(newMatchCode < matchCode);\n                    matchCode = newMatchCode;\n                    if (unlikely(ip <= filledIp)) {\n                        /* We have already filled up to filledIp so if ip ends up less than filledIp\n                         * we have positions in the hash table beyond the current position. This is\n                         * a problem if we reuse the hash table. So we have to remove these positions\n                         * from the hash table.\n                         */\n                        const BYTE* ptr;\n                        DEBUGLOG(5, \"Clearing %u positions\", (U32)(filledIp - ip));\n                        for (ptr = ip; ptr <= filledIp; ++ptr) {\n                            U32 const h = LZ4_hashPosition(ptr, tableType);\n                            LZ4_clearHash(h, cctx->hashTable, tableType);\n                        }\n                    }\n                } else {\n                    assert(outputDirective == limitedOutput);\n                    return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n                }\n            }\n            if (matchCode >= ML_MASK) {\n                *token += ML_MASK;\n                matchCode -= ML_MASK;\n                LZ4_write32(op, 0xFFFFFFFF);\n                while (matchCode >= 4*255) {\n                    op+=4;\n                    LZ4_write32(op, 0xFFFFFFFF);\n                    matchCode -= 4*255;\n                }\n                op += matchCode / 255;\n                *op++ = (BYTE)(matchCode % 255);\n            } else\n                *token += (BYTE)(matchCode);\n        }\n        /* Ensure we have enough space for the last literals. */\n        assert(!(outputDirective == fillOutput && op + 1 + LASTLITERALS > olimit));\n\n        anchor = ip;\n\n        /* Test end of chunk */\n        if (ip >= mflimitPlusOne) break;\n\n        /* Fill table */\n        LZ4_putPosition(ip-2, cctx->hashTable, tableType, base);\n\n        /* Test next position */\n        if (tableType == byPtr) {\n\n            match = LZ4_getPosition(ip, cctx->hashTable, tableType, base);\n            LZ4_putPosition(ip, cctx->hashTable, tableType, base);\n            if ( (match+LZ4_DISTANCE_MAX >= ip)\n              && (LZ4_read32(match) == LZ4_read32(ip)) )\n            { token=op++; *token=0; goto _next_match; }\n\n        } else {   /* byU32, byU16 */\n\n            U32 const h = LZ4_hashPosition(ip, tableType);\n            U32 const current = (U32)(ip-base);\n            U32 matchIndex = LZ4_getIndexOnHash(h, cctx->hashTable, tableType);\n            assert(matchIndex < current);\n            if (dictDirective == usingDictCtx) {\n                if (matchIndex < startIndex) {\n                    /* there was no match, try the dictionary */\n                    matchIndex = LZ4_getIndexOnHash(h, dictCtx->hashTable, byU32);\n                    match = dictBase + matchIndex;\n                    lowLimit = dictionary;   /* required for match length counter */\n                    matchIndex += dictDelta;\n                } else {\n                    match = base + matchIndex;\n                    lowLimit = (const BYTE*)source;  /* required for match length counter */\n                }\n            } else if (dictDirective==usingExtDict) {\n                if (matchIndex < startIndex) {\n                    assert(dictBase);\n                    match = dictBase + matchIndex;\n                    lowLimit = dictionary;   /* required for match length counter */\n                } else {\n                    match = base + matchIndex;\n                    lowLimit = (const BYTE*)source;   /* required for match length counter */\n                }\n            } else {   /* single memory segment */\n                match = base + matchIndex;\n            }\n            LZ4_putIndexOnHash(current, h, cctx->hashTable, tableType);\n            assert(matchIndex < current);\n            if ( ((dictIssue==dictSmall) ? (matchIndex >= prefixIdxLimit) : 1)\n              && (((tableType==byU16) && (LZ4_DISTANCE_MAX == LZ4_DISTANCE_ABSOLUTE_MAX)) ? 1 : (matchIndex+LZ4_DISTANCE_MAX >= current))\n              && (LZ4_read32(match) == LZ4_read32(ip)) ) {\n                token=op++;\n                *token=0;\n                if (maybe_extMem) offset = current - matchIndex;\n                DEBUGLOG(6, \"seq.start:%i, literals=%u, match.start:%i\",\n                            (int)(anchor-(const BYTE*)source), 0, (int)(ip-(const BYTE*)source));\n                goto _next_match;\n            }\n        }\n\n        /* Prepare next loop */\n        forwardH = LZ4_hashPosition(++ip, tableType);\n\n    }\n\n_last_literals:\n    /* Encode Last Literals */\n    {   size_t lastRun = (size_t)(iend - anchor);\n        if ( (outputDirective) &&  /* Check output buffer overflow */\n            (op + lastRun + 1 + ((lastRun+255-RUN_MASK)/255) > olimit)) {\n            if (outputDirective == fillOutput) {\n                /* adapt lastRun to fill 'dst' */\n                assert(olimit >= op);\n                lastRun  = (size_t)(olimit-op) - 1/*token*/;\n                lastRun -= (lastRun + 256 - RUN_MASK) / 256;  /*additional length tokens*/\n            } else {\n                assert(outputDirective == limitedOutput);\n                return 0;   /* cannot compress within `dst` budget. Stored indexes in hash table are nonetheless fine */\n            }\n        }\n        DEBUGLOG(6, \"Final literal run : %i literals\", (int)lastRun);\n        if (lastRun >= RUN_MASK) {\n            size_t accumulator = lastRun - RUN_MASK;\n            *op++ = RUN_MASK << ML_BITS;\n            for(; accumulator >= 255 ; accumulator-=255) *op++ = 255;\n            *op++ = (BYTE) accumulator;\n        } else {\n            *op++ = (BYTE)(lastRun<<ML_BITS);\n        }\n        LZ4_memcpy(op, anchor, lastRun);\n        ip = anchor + lastRun;\n        op += lastRun;\n    }\n\n    if (outputDirective == fillOutput) {\n        *inputConsumed = (int) (((const char*)ip)-source);\n    }\n    result = (int)(((char*)op) - dest);\n    assert(result > 0);\n    DEBUGLOG(5, \"LZ4_compress_generic: compressed %i bytes into %i bytes\", inputSize, result);\n    return result;\n}\n\n/** LZ4_compress_generic() :\n *  inlined, to ensure branches are decided at compilation time;\n *  takes care of src == (NULL, 0)\n *  and forward the rest to LZ4_compress_generic_validated */\nLZ4_FORCE_INLINE int LZ4_compress_generic(\n                 LZ4_stream_t_internal* const cctx,\n                 const char* const src,\n                 char* const dst,\n                 const int srcSize,\n                 int *inputConsumed, /* only written when outputDirective == fillOutput */\n                 const int dstCapacity,\n                 const limitedOutput_directive outputDirective,\n                 const tableType_t tableType,\n                 const dict_directive dictDirective,\n                 const dictIssue_directive dictIssue,\n                 const int acceleration)\n{\n    DEBUGLOG(5, \"LZ4_compress_generic: srcSize=%i, dstCapacity=%i\",\n                srcSize, dstCapacity);\n\n    if ((U32)srcSize > (U32)LZ4_MAX_INPUT_SIZE) { return 0; }  /* Unsupported srcSize, too large (or negative) */\n    if (srcSize == 0) {   /* src == NULL supported if srcSize == 0 */\n        if (outputDirective != notLimited && dstCapacity <= 0) return 0;  /* no output, can't write anything */\n        DEBUGLOG(5, \"Generating an empty block\");\n        assert(outputDirective == notLimited || dstCapacity >= 1);\n        assert(dst != NULL);\n        dst[0] = 0;\n        if (outputDirective == fillOutput) {\n            assert (inputConsumed != NULL);\n            *inputConsumed = 0;\n        }\n        return 1;\n    }\n    assert(src != NULL);\n\n    return LZ4_compress_generic_validated(cctx, src, dst, srcSize,\n                inputConsumed, /* only written into if outputDirective == fillOutput */\n                dstCapacity, outputDirective,\n                tableType, dictDirective, dictIssue, acceleration);\n}\n\n\nint LZ4_compress_fast_extState(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)\n{\n    LZ4_stream_t_internal* const ctx = & LZ4_initStream(state, sizeof(LZ4_stream_t)) -> internal_donotuse;\n    assert(ctx != NULL);\n    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;\n    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;\n    if (maxOutputSize >= LZ4_compressBound(inputSize)) {\n        if (inputSize < LZ4_64Klimit) {\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, byU16, noDict, noDictIssue, acceleration);\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n        }\n    } else {\n        if (inputSize < LZ4_64Klimit) {\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, byU16, noDict, noDictIssue, acceleration);\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)source > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return LZ4_compress_generic(ctx, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n        }\n    }\n}\n\n/**\n * LZ4_compress_fast_extState_fastReset() :\n * A variant of LZ4_compress_fast_extState().\n *\n * Using this variant avoids an expensive initialization step. It is only safe\n * to call if the state buffer is known to be correctly initialized already\n * (see comment in lz4.h on LZ4_resetStream_fast() for a definition of\n * \"correctly initialized\").\n */\nint LZ4_compress_fast_extState_fastReset(void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration)\n{\n    LZ4_stream_t_internal* ctx = &((LZ4_stream_t*)state)->internal_donotuse;\n    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;\n    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;\n\n    if (dstCapacity >= LZ4_compressBound(srcSize)) {\n        if (srcSize < LZ4_64Klimit) {\n            const tableType_t tableType = byU16;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            if (ctx->currentOffset) {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, dictSmall, acceleration);\n            } else {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n            }\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, 0, notLimited, tableType, noDict, noDictIssue, acceleration);\n        }\n    } else {\n        if (srcSize < LZ4_64Klimit) {\n            const tableType_t tableType = byU16;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            if (ctx->currentOffset) {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, dictSmall, acceleration);\n            } else {\n                return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n            }\n        } else {\n            const tableType_t tableType = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            LZ4_prepareTable(ctx, srcSize, tableType);\n            return LZ4_compress_generic(ctx, src, dst, srcSize, NULL, dstCapacity, limitedOutput, tableType, noDict, noDictIssue, acceleration);\n        }\n    }\n}\n\n\nint LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutputSize, int acceleration)\n{\n    int result;\n#if (LZ4_HEAPMODE)\n    LZ4_stream_t* ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */\n    if (ctxPtr == NULL) return 0;\n#else\n    LZ4_stream_t ctx;\n    LZ4_stream_t* const ctxPtr = &ctx;\n#endif\n    result = LZ4_compress_fast_extState(ctxPtr, source, dest, inputSize, maxOutputSize, acceleration);\n\n#if (LZ4_HEAPMODE)\n    FREEMEM(ctxPtr);\n#endif\n    return result;\n}\n\n\nint LZ4_compress_default(const char* src, char* dst, int srcSize, int maxOutputSize)\n{\n    return LZ4_compress_fast(src, dst, srcSize, maxOutputSize, 1);\n}\n\n\n/* Note!: This function leaves the stream in an unclean/broken state!\n * It is not safe to subsequently use the same state with a _fastReset() or\n * _continue() call without resetting it. */\nstatic int LZ4_compress_destSize_extState (LZ4_stream_t* state, const char* src, char* dst, int* srcSizePtr, int targetDstSize)\n{\n    void* const s = LZ4_initStream(state, sizeof (*state));\n    assert(s != NULL); (void)s;\n\n    if (targetDstSize >= LZ4_compressBound(*srcSizePtr)) {  /* compression success is guaranteed */\n        return LZ4_compress_fast_extState(state, src, dst, *srcSizePtr, targetDstSize, 1);\n    } else {\n        if (*srcSizePtr < LZ4_64Klimit) {\n            return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, byU16, noDict, noDictIssue, 1);\n        } else {\n            tableType_t const addrMode = ((sizeof(void*)==4) && ((uptrval)src > LZ4_DISTANCE_MAX)) ? byPtr : byU32;\n            return LZ4_compress_generic(&state->internal_donotuse, src, dst, *srcSizePtr, srcSizePtr, targetDstSize, fillOutput, addrMode, noDict, noDictIssue, 1);\n    }   }\n}\n\n\nint LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targetDstSize)\n{\n#if (LZ4_HEAPMODE)\n    LZ4_stream_t* ctx = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));   /* malloc-calloc always properly aligned */\n    if (ctx == NULL) return 0;\n#else\n    LZ4_stream_t ctxBody;\n    LZ4_stream_t* ctx = &ctxBody;\n#endif\n\n    int result = LZ4_compress_destSize_extState(ctx, src, dst, srcSizePtr, targetDstSize);\n\n#if (LZ4_HEAPMODE)\n    FREEMEM(ctx);\n#endif\n    return result;\n}\n\n\n\n/*-******************************\n*  Streaming functions\n********************************/\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4_stream_t* LZ4_createStream(void)\n{\n    LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t));\n    LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal));\n    DEBUGLOG(4, \"LZ4_createStream %p\", lz4s);\n    if (lz4s == NULL) return NULL;\n    LZ4_initStream(lz4s, sizeof(*lz4s));\n    return lz4s;\n}\n#endif\n\nstatic size_t LZ4_stream_t_alignment(void)\n{\n#if LZ4_ALIGN_TEST\n    typedef struct { char c; LZ4_stream_t t; } t_a;\n    return sizeof(t_a) - sizeof(LZ4_stream_t);\n#else\n    return 1;  /* effectively disabled */\n#endif\n}\n\nLZ4_stream_t* LZ4_initStream (void* buffer, size_t size)\n{\n    DEBUGLOG(5, \"LZ4_initStream\");\n    if (buffer == NULL) { return NULL; }\n    if (size < sizeof(LZ4_stream_t)) { return NULL; }\n    if (!LZ4_isAligned(buffer, LZ4_stream_t_alignment())) return NULL;\n    MEM_INIT(buffer, 0, sizeof(LZ4_stream_t_internal));\n    return (LZ4_stream_t*)buffer;\n}\n\n/* resetStream is now deprecated,\n * prefer initStream() which is more general */\nvoid LZ4_resetStream (LZ4_stream_t* LZ4_stream)\n{\n    DEBUGLOG(5, \"LZ4_resetStream (ctx:%p)\", LZ4_stream);\n    MEM_INIT(LZ4_stream, 0, sizeof(LZ4_stream_t_internal));\n}\n\nvoid LZ4_resetStream_fast(LZ4_stream_t* ctx) {\n    LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32);\n}\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nint LZ4_freeStream (LZ4_stream_t* LZ4_stream)\n{\n    if (!LZ4_stream) return 0;   /* support free on NULL */\n    DEBUGLOG(5, \"LZ4_freeStream %p\", LZ4_stream);\n    FREEMEM(LZ4_stream);\n    return (0);\n}\n#endif\n\n\n#define HASH_UNIT sizeof(reg_t)\nint LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize)\n{\n    LZ4_stream_t_internal* dict = &LZ4_dict->internal_donotuse;\n    const tableType_t tableType = byU32;\n    const BYTE* p = (const BYTE*)dictionary;\n    const BYTE* const dictEnd = p + dictSize;\n    const BYTE* base;\n\n    DEBUGLOG(4, \"LZ4_loadDict (%i bytes from %p into %p)\", dictSize, dictionary, LZ4_dict);\n\n    /* It's necessary to reset the context,\n     * and not just continue it with prepareTable()\n     * to avoid any risk of generating overflowing matchIndex\n     * when compressing using this dictionary */\n    LZ4_resetStream(LZ4_dict);\n\n    /* We always increment the offset by 64 KB, since, if the dict is longer,\n     * we truncate it to the last 64k, and if it's shorter, we still want to\n     * advance by a whole window length so we can provide the guarantee that\n     * there are only valid offsets in the window, which allows an optimization\n     * in LZ4_compress_fast_continue() where it uses noDictIssue even when the\n     * dictionary isn't a full 64k. */\n    dict->currentOffset += 64 KB;\n\n    if (dictSize < (int)HASH_UNIT) {\n        return 0;\n    }\n\n    if ((dictEnd - p) > 64 KB) p = dictEnd - 64 KB;\n    base = dictEnd - dict->currentOffset;\n    dict->dictionary = p;\n    dict->dictSize = (U32)(dictEnd - p);\n    dict->tableType = (U32)tableType;\n\n    while (p <= dictEnd-HASH_UNIT) {\n        LZ4_putPosition(p, dict->hashTable, tableType, base);\n        p+=3;\n    }\n\n    return (int)dict->dictSize;\n}\n\nvoid LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream)\n{\n    const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL :\n        &(dictionaryStream->internal_donotuse);\n\n    DEBUGLOG(4, \"LZ4_attach_dictionary (%p, %p, size %u)\",\n             workingStream, dictionaryStream,\n             dictCtx != NULL ? dictCtx->dictSize : 0);\n\n    if (dictCtx != NULL) {\n        /* If the current offset is zero, we will never look in the\n         * external dictionary context, since there is no value a table\n         * entry can take that indicate a miss. In that case, we need\n         * to bump the offset to something non-zero.\n         */\n        if (workingStream->internal_donotuse.currentOffset == 0) {\n            workingStream->internal_donotuse.currentOffset = 64 KB;\n        }\n\n        /* Don't actually attach an empty dictionary.\n         */\n        if (dictCtx->dictSize == 0) {\n            dictCtx = NULL;\n        }\n    }\n    workingStream->internal_donotuse.dictCtx = dictCtx;\n}\n\n\nstatic void LZ4_renormDictT(LZ4_stream_t_internal* LZ4_dict, int nextSize)\n{\n    assert(nextSize >= 0);\n    if (LZ4_dict->currentOffset + (unsigned)nextSize > 0x80000000) {   /* potential ptrdiff_t overflow (32-bits mode) */\n        /* rescale hash table */\n        U32 const delta = LZ4_dict->currentOffset - 64 KB;\n        const BYTE* dictEnd = LZ4_dict->dictionary + LZ4_dict->dictSize;\n        int i;\n        DEBUGLOG(4, \"LZ4_renormDictT\");\n        for (i=0; i<LZ4_HASH_SIZE_U32; i++) {\n            if (LZ4_dict->hashTable[i] < delta) LZ4_dict->hashTable[i]=0;\n            else LZ4_dict->hashTable[i] -= delta;\n        }\n        LZ4_dict->currentOffset = 64 KB;\n        if (LZ4_dict->dictSize > 64 KB) LZ4_dict->dictSize = 64 KB;\n        LZ4_dict->dictionary = dictEnd - LZ4_dict->dictSize;\n    }\n}\n\n\nint LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream,\n                                const char* source, char* dest,\n                                int inputSize, int maxOutputSize,\n                                int acceleration)\n{\n    const tableType_t tableType = byU32;\n    LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse;\n    const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL;\n\n    DEBUGLOG(5, \"LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)\", inputSize, streamPtr->dictSize);\n\n    LZ4_renormDictT(streamPtr, inputSize);   /* fix index overflow */\n    if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT;\n    if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX;\n\n    /* invalidate tiny dictionaries */\n    if ( (streamPtr->dictSize < 4)     /* tiny dictionary : not enough for a hash */\n      && (dictEnd != source)           /* prefix mode */\n      && (inputSize > 0)               /* tolerance : don't lose history, in case next invocation would use prefix mode */\n      && (streamPtr->dictCtx == NULL)  /* usingDictCtx */\n      ) {\n        DEBUGLOG(5, \"LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small\", streamPtr->dictSize, streamPtr->dictionary);\n        /* remove dictionary existence from history, to employ faster prefix mode */\n        streamPtr->dictSize = 0;\n        streamPtr->dictionary = (const BYTE*)source;\n        dictEnd = source;\n    }\n\n    /* Check overlapping input/dictionary space */\n    {   const char* const sourceEnd = source + inputSize;\n        if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) {\n            streamPtr->dictSize = (U32)(dictEnd - sourceEnd);\n            if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB;\n            if (streamPtr->dictSize < 4) streamPtr->dictSize = 0;\n            streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize;\n        }\n    }\n\n    /* prefix mode : source data follows dictionary */\n    if (dictEnd == source) {\n        if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset))\n            return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration);\n        else\n            return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, noDictIssue, acceleration);\n    }\n\n    /* external dictionary mode */\n    {   int result;\n        if (streamPtr->dictCtx) {\n            /* We depend here on the fact that dictCtx'es (produced by\n             * LZ4_loadDict) guarantee that their tables contain no references\n             * to offsets between dictCtx->currentOffset - 64 KB and\n             * dictCtx->currentOffset - dictCtx->dictSize. This makes it safe\n             * to use noDictIssue even when the dict isn't a full 64 KB.\n             */\n            if (inputSize > 4 KB) {\n                /* For compressing large blobs, it is faster to pay the setup\n                 * cost to copy the dictionary's tables into the active context,\n                 * so that the compression loop is only looking into one table.\n                 */\n                LZ4_memcpy(streamPtr, streamPtr->dictCtx, sizeof(*streamPtr));\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);\n            } else {\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration);\n            }\n        } else {  /* small data <= 4 KB */\n            if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration);\n            } else {\n                result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, noDictIssue, acceleration);\n            }\n        }\n        streamPtr->dictionary = (const BYTE*)source;\n        streamPtr->dictSize = (U32)inputSize;\n        return result;\n    }\n}\n\n\n/* Hidden debug function, to force-test external dictionary mode */\nint LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* dest, int srcSize)\n{\n    LZ4_stream_t_internal* streamPtr = &LZ4_dict->internal_donotuse;\n    int result;\n\n    LZ4_renormDictT(streamPtr, srcSize);\n\n    if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) {\n        result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, dictSmall, 1);\n    } else {\n        result = LZ4_compress_generic(streamPtr, source, dest, srcSize, NULL, 0, notLimited, byU32, usingExtDict, noDictIssue, 1);\n    }\n\n    streamPtr->dictionary = (const BYTE*)source;\n    streamPtr->dictSize = (U32)srcSize;\n\n    return result;\n}\n\n\n/*! LZ4_saveDict() :\n *  If previously compressed data block is not guaranteed to remain available at its memory location,\n *  save it into a safer place (char* safeBuffer).\n *  Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable,\n *         one can therefore call LZ4_compress_fast_continue() right after.\n * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error.\n */\nint LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize)\n{\n    LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse;\n\n    DEBUGLOG(5, \"LZ4_saveDict : dictSize=%i, safeBuffer=%p\", dictSize, safeBuffer);\n\n    if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */\n    if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; }\n\n    if (safeBuffer == NULL) assert(dictSize == 0);\n    if (dictSize > 0) {\n        const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize;\n        assert(dict->dictionary);\n        LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize);\n    }\n\n    dict->dictionary = (const BYTE*)safeBuffer;\n    dict->dictSize = (U32)dictSize;\n\n    return dictSize;\n}\n\n\n\n/*-*******************************\n *  Decompression functions\n ********************************/\n\ntypedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive;\n\n#undef MIN\n#define MIN(a,b)    ( (a) < (b) ? (a) : (b) )\n\n\n/* variant for decompress_unsafe()\n * does not know end of input\n * presumes input is well formed\n * note : will consume at least one byte */\nsize_t read_long_length_no_check(const BYTE** pp)\n{\n    size_t b, l = 0;\n    do { b = **pp; (*pp)++; l += b; } while (b==255);\n    DEBUGLOG(6, \"read_long_length_no_check: +length=%zu using %zu input bytes\", l, l/255 + 1)\n    return l;\n}\n\n/* core decoder variant for LZ4_decompress_fast*()\n * for legacy support only : these entry points are deprecated.\n * - Presumes input is correctly formed (no defense vs malformed inputs)\n * - Does not know input size (presume input buffer is \"large enough\")\n * - Decompress a full block (only)\n * @return : nb of bytes read from input.\n * Note : this variant is not optimized for speed, just for maintenance.\n *        the goal is to remove support of decompress_fast*() variants by v2.0\n**/\nLZ4_FORCE_INLINE int\nLZ4_decompress_unsafe_generic(\n                 const BYTE* const istart,\n                 BYTE* const ostart,\n                 int decompressedSize,\n\n                 size_t prefixSize,\n                 const BYTE* const dictStart,  /* only if dict==usingExtDict */\n                 const size_t dictSize         /* note: =0 if dictStart==NULL */\n                 )\n{\n    const BYTE* ip = istart;\n    BYTE* op = (BYTE*)ostart;\n    BYTE* const oend = ostart + decompressedSize;\n    const BYTE* const prefixStart = ostart - prefixSize;\n\n    DEBUGLOG(5, \"LZ4_decompress_unsafe_generic\");\n    if (dictStart == NULL) assert(dictSize == 0);\n\n    while (1) {\n        /* start new sequence */\n        unsigned token = *ip++;\n\n        /* literals */\n        {   size_t ll = token >> ML_BITS;\n            if (ll==15) {\n                /* long literal length */\n                ll += read_long_length_no_check(&ip);\n            }\n            if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */\n            LZ4_memmove(op, ip, ll); /* support in-place decompression */\n            op += ll;\n            ip += ll;\n            if ((size_t)(oend-op) < MFLIMIT) {\n                if (op==oend) break;  /* end of block */\n                DEBUGLOG(5, \"invalid: literals end at distance %zi from end of block\", oend-op);\n                /* incorrect end of block :\n                 * last match must start at least MFLIMIT==12 bytes before end of output block */\n                return -1;\n        }   }\n\n        /* match */\n        {   size_t ml = token & 15;\n            size_t const offset = LZ4_readLE16(ip);\n            ip+=2;\n\n            if (ml==15) {\n                /* long literal length */\n                ml += read_long_length_no_check(&ip);\n            }\n            ml += MINMATCH;\n\n            if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */\n\n            {   const BYTE* match = op - offset;\n\n                /* out of range */\n                if (offset > (size_t)(op - prefixStart) + dictSize) {\n                    DEBUGLOG(6, \"offset out of range\");\n                    return -1;\n                }\n\n                /* check special case : extDict */\n                if (offset > (size_t)(op - prefixStart)) {\n                    /* extDict scenario */\n                    const BYTE* const dictEnd = dictStart + dictSize;\n                    const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart));\n                    size_t const extml = (size_t)(dictEnd - extMatch);\n                    if (extml > ml) {\n                        /* match entirely within extDict */\n                        LZ4_memmove(op, extMatch, ml);\n                        op += ml;\n                        ml = 0;\n                    } else {\n                        /* match split between extDict & prefix */\n                        LZ4_memmove(op, extMatch, extml);\n                        op += extml;\n                        ml -= extml;\n                    }\n                    match = prefixStart;\n                }\n\n                /* match copy - slow variant, supporting overlap copy */\n                {   size_t u;\n                    for (u=0; u<ml; u++) {\n                        op[u] = match[u];\n            }   }   }\n            op += ml;\n            if ((size_t)(oend-op) < LASTLITERALS) {\n                DEBUGLOG(5, \"invalid: match ends at distance %zi from end of block\", oend-op);\n                /* incorrect end of block :\n                 * last match must stop at least LASTLITERALS==5 bytes before end of output block */\n                return -1;\n            }\n        } /* match */\n    } /* main loop */\n    return (int)(ip - istart);\n}\n\n\n/* Read the variable-length literal or match length.\n *\n * @ip : input pointer\n * @ilimit : position after which if length is not decoded, the input is necessarily corrupted.\n * @initial_check - check ip >= ipmax before start of loop.  Returns initial_error if so.\n * @error (output) - error code.  Must be set to 0 before call.\n**/\ntypedef size_t Rvl_t;\nstatic const Rvl_t rvl_error = (Rvl_t)(-1);\nLZ4_FORCE_INLINE Rvl_t\nread_variable_length(const BYTE** ip, const BYTE* ilimit,\n                     int initial_check)\n{\n    Rvl_t s, length = 0;\n    assert(ip != NULL);\n    assert(*ip !=  NULL);\n    assert(ilimit != NULL);\n    if (initial_check && unlikely((*ip) >= ilimit)) {    /* read limit reached */\n        return rvl_error;\n    }\n    do {\n        s = **ip;\n        (*ip)++;\n        length += s;\n        if (unlikely((*ip) > ilimit)) {    /* read limit reached */\n            return rvl_error;\n        }\n        /* accumulator overflow detection (32-bit mode only) */\n        if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) {\n            return rvl_error;\n        }\n    } while (s==255);\n\n    return length;\n}\n\n/*! LZ4_decompress_generic() :\n *  This generic decompression function covers all use cases.\n *  It shall be instantiated several times, using different sets of directives.\n *  Note that it is important for performance that this function really get inlined,\n *  in order to remove useless branches during compilation optimization.\n */\nLZ4_FORCE_INLINE int\nLZ4_decompress_generic(\n                 const char* const src,\n                 char* const dst,\n                 int srcSize,\n                 int outputSize,         /* If endOnInput==endOnInputSize, this value is `dstCapacity` */\n\n                 earlyEnd_directive partialDecoding,  /* full, partial */\n                 dict_directive dict,                 /* noDict, withPrefix64k, usingExtDict */\n                 const BYTE* const lowPrefix,  /* always <= dst, == dst when no prefix */\n                 const BYTE* const dictStart,  /* only if dict==usingExtDict */\n                 const size_t dictSize         /* note : = 0 if noDict */\n                 )\n{\n    if ((src == NULL) || (outputSize < 0)) { return -1; }\n\n    {   const BYTE* ip = (const BYTE*) src;\n        const BYTE* const iend = ip + srcSize;\n\n        BYTE* op = (BYTE*) dst;\n        BYTE* const oend = op + outputSize;\n        BYTE* cpy;\n\n        const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize;\n\n        const int checkOffset = (dictSize < (int)(64 KB));\n\n\n        /* Set up the \"end\" pointers for the shortcut. */\n        const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/;\n        const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/;\n\n        const BYTE* match;\n        size_t offset;\n        unsigned token;\n        size_t length;\n\n\n        DEBUGLOG(5, \"LZ4_decompress_generic (srcSize:%i, dstSize:%i)\", srcSize, outputSize);\n\n        /* Special cases */\n        assert(lowPrefix <= op);\n        if (unlikely(outputSize==0)) {\n            /* Empty output buffer */\n            if (partialDecoding) return 0;\n            return ((srcSize==1) && (*ip==0)) ? 0 : -1;\n        }\n        if (unlikely(srcSize==0)) { return -1; }\n\n    /* LZ4_FAST_DEC_LOOP:\n     * designed for modern OoO performance cpus,\n     * where copying reliably 32-bytes is preferable to an unpredictable branch.\n     * note : fast loop may show a regression for some client arm chips. */\n#if LZ4_FAST_DEC_LOOP\n        if ((oend - op) < FASTLOOP_SAFE_DISTANCE) {\n            DEBUGLOG(6, \"skip fast decode loop\");\n            goto safe_decode;\n        }\n\n        /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */\n        while (1) {\n            /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */\n            assert(oend - op >= FASTLOOP_SAFE_DISTANCE);\n            assert(ip < iend);\n            token = *ip++;\n            length = token >> ML_BITS;  /* literal length */\n\n            /* decode literal length */\n            if (length == RUN_MASK) {\n                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */\n                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */\n\n                /* copy literals */\n                cpy = op+length;\n                LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);\n                if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; }\n                LZ4_wildCopy32(op, ip, cpy);\n                ip += length; op = cpy;\n            } else {\n                cpy = op+length;\n                DEBUGLOG(7, \"copy %u bytes in a 16-bytes stripe\", (unsigned)length);\n                /* We don't need to check oend, since we check it once for each loop below */\n                if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; }\n                /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */\n                LZ4_memcpy(op, ip, 16);\n                ip += length; op = cpy;\n            }\n\n            /* get offset */\n            offset = LZ4_readLE16(ip); ip+=2;\n            match = op - offset;\n            assert(match <= op);  /* overflow check */\n\n            /* get matchlength */\n            length = token & ML_MASK;\n\n            if (length == ML_MASK) {\n                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                length += MINMATCH;\n                if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */\n                if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */\n                if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {\n                    goto safe_match_copy;\n                }\n            } else {\n                length += MINMATCH;\n                if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) {\n                    goto safe_match_copy;\n                }\n\n                /* Fastpath check: skip LZ4_wildCopy32 when true */\n                if ((dict == withPrefix64k) || (match >= lowPrefix)) {\n                    if (offset >= 8) {\n                        assert(match >= lowPrefix);\n                        assert(match <= op);\n                        assert(op + 18 <= oend);\n\n                        LZ4_memcpy(op, match, 8);\n                        LZ4_memcpy(op+8, match+8, 8);\n                        LZ4_memcpy(op+16, match+16, 2);\n                        op += length;\n                        continue;\n            }   }   }\n\n            if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */\n            /* match starting within external dictionary */\n            if ((dict==usingExtDict) && (match < lowPrefix)) {\n                assert(dictEnd != NULL);\n                if (unlikely(op+length > oend-LASTLITERALS)) {\n                    if (partialDecoding) {\n                        DEBUGLOG(7, \"partialDecoding: dictionary match, close to dstEnd\");\n                        length = MIN(length, (size_t)(oend-op));\n                    } else {\n                        goto _output_error;  /* end-of-block condition violated */\n                }   }\n\n                if (length <= (size_t)(lowPrefix-match)) {\n                    /* match fits entirely within external dictionary : just copy */\n                    LZ4_memmove(op, dictEnd - (lowPrefix-match), length);\n                    op += length;\n                } else {\n                    /* match stretches into both external dictionary and current block */\n                    size_t const copySize = (size_t)(lowPrefix - match);\n                    size_t const restSize = length - copySize;\n                    LZ4_memcpy(op, dictEnd - copySize, copySize);\n                    op += copySize;\n                    if (restSize > (size_t)(op - lowPrefix)) {  /* overlap copy */\n                        BYTE* const endOfMatch = op + restSize;\n                        const BYTE* copyFrom = lowPrefix;\n                        while (op < endOfMatch) { *op++ = *copyFrom++; }\n                    } else {\n                        LZ4_memcpy(op, lowPrefix, restSize);\n                        op += restSize;\n                }   }\n                continue;\n            }\n\n            /* copy match within block */\n            cpy = op + length;\n\n            assert((op <= oend) && (oend-op >= 32));\n            if (unlikely(offset<16)) {\n                LZ4_memcpy_using_offset(op, match, cpy, offset);\n            } else {\n                LZ4_wildCopy32(op, match, cpy);\n            }\n\n            op = cpy;   /* wildcopy correction */\n        }\n    safe_decode:\n#endif\n\n        /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */\n        while (1) {\n            assert(ip < iend);\n            token = *ip++;\n            length = token >> ML_BITS;  /* literal length */\n\n            /* A two-stage shortcut for the most common case:\n             * 1) If the literal length is 0..14, and there is enough space,\n             * enter the shortcut and copy 16 bytes on behalf of the literals\n             * (in the fast mode, only 8 bytes can be safely copied this way).\n             * 2) Further if the match length is 4..18, copy 18 bytes in a similar\n             * manner; but we ensure that there's enough space in the output for\n             * those 18 bytes earlier, upon entering the shortcut (in other words,\n             * there is a combined check for both stages).\n             */\n            if ( (length != RUN_MASK)\n                /* strictly \"less than\" on input, to re-enter the loop with at least one byte */\n              && likely((ip < shortiend) & (op <= shortoend)) ) {\n                /* Copy the literals */\n                LZ4_memcpy(op, ip, 16);\n                op += length; ip += length;\n\n                /* The second stage: prepare for match copying, decode full info.\n                 * If it doesn't work out, the info won't be wasted. */\n                length = token & ML_MASK; /* match length */\n                offset = LZ4_readLE16(ip); ip += 2;\n                match = op - offset;\n                assert(match <= op); /* check overflow */\n\n                /* Do not deal with overlapping matches. */\n                if ( (length != ML_MASK)\n                  && (offset >= 8)\n                  && (dict==withPrefix64k || match >= lowPrefix) ) {\n                    /* Copy the match. */\n                    LZ4_memcpy(op + 0, match + 0, 8);\n                    LZ4_memcpy(op + 8, match + 8, 8);\n                    LZ4_memcpy(op +16, match +16, 2);\n                    op += length + MINMATCH;\n                    /* Both stages worked, load the next token. */\n                    continue;\n                }\n\n                /* The second stage didn't work out, but the info is ready.\n                 * Propel it right to the point of match copying. */\n                goto _copy_match;\n            }\n\n            /* decode literal length */\n            if (length == RUN_MASK) {\n                size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */\n                if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */\n            }\n\n            /* copy literals */\n            cpy = op+length;\n#if LZ4_FAST_DEC_LOOP\n        safe_literal_copy:\n#endif\n            LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH);\n            if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) {\n                /* We've either hit the input parsing restriction or the output parsing restriction.\n                 * In the normal scenario, decoding a full block, it must be the last sequence,\n                 * otherwise it's an error (invalid input or dimensions).\n                 * In partialDecoding scenario, it's necessary to ensure there is no buffer overflow.\n                 */\n                if (partialDecoding) {\n                    /* Since we are partial decoding we may be in this block because of the output parsing\n                     * restriction, which is not valid since the output buffer is allowed to be undersized.\n                     */\n                    DEBUGLOG(7, \"partialDecoding: copying literals, close to input or output end\")\n                    DEBUGLOG(7, \"partialDecoding: literal length = %u\", (unsigned)length);\n                    DEBUGLOG(7, \"partialDecoding: remaining space in dstBuffer : %i\", (int)(oend - op));\n                    DEBUGLOG(7, \"partialDecoding: remaining space in srcBuffer : %i\", (int)(iend - ip));\n                    /* Finishing in the middle of a literals segment,\n                     * due to lack of input.\n                     */\n                    if (ip+length > iend) {\n                        length = (size_t)(iend-ip);\n                        cpy = op + length;\n                    }\n                    /* Finishing in the middle of a literals segment,\n                     * due to lack of output space.\n                     */\n                    if (cpy > oend) {\n                        cpy = oend;\n                        assert(op<=oend);\n                        length = (size_t)(oend-op);\n                    }\n                } else {\n                     /* We must be on the last sequence (or invalid) because of the parsing limitations\n                      * so check that we exactly consume the input and don't overrun the output buffer.\n                      */\n                    if ((ip+length != iend) || (cpy > oend)) {\n                        DEBUGLOG(6, \"should have been last run of literals\")\n                        DEBUGLOG(6, \"ip(%p) + length(%i) = %p != iend (%p)\", ip, (int)length, ip+length, iend);\n                        DEBUGLOG(6, \"or cpy(%p) > oend(%p)\", cpy, oend);\n                        goto _output_error;\n                    }\n                }\n                LZ4_memmove(op, ip, length);  /* supports overlapping memory regions, for in-place decompression scenarios */\n                ip += length;\n                op += length;\n                /* Necessarily EOF when !partialDecoding.\n                 * When partialDecoding, it is EOF if we've either\n                 * filled the output buffer or\n                 * can't proceed with reading an offset for following match.\n                 */\n                if (!partialDecoding || (cpy == oend) || (ip >= (iend-2))) {\n                    break;\n                }\n            } else {\n                LZ4_wildCopy8(op, ip, cpy);   /* can overwrite up to 8 bytes beyond cpy */\n                ip += length; op = cpy;\n            }\n\n            /* get offset */\n            offset = LZ4_readLE16(ip); ip+=2;\n            match = op - offset;\n\n            /* get matchlength */\n            length = token & ML_MASK;\n\n    _copy_match:\n            if (length == ML_MASK) {\n                size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0);\n                if (addl == rvl_error) { goto _output_error; }\n                length += addl;\n                if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error;   /* overflow detection */\n            }\n            length += MINMATCH;\n\n#if LZ4_FAST_DEC_LOOP\n        safe_match_copy:\n#endif\n            if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error;   /* Error : offset outside buffers */\n            /* match starting within external dictionary */\n            if ((dict==usingExtDict) && (match < lowPrefix)) {\n                assert(dictEnd != NULL);\n                if (unlikely(op+length > oend-LASTLITERALS)) {\n                    if (partialDecoding) length = MIN(length, (size_t)(oend-op));\n                    else goto _output_error;   /* doesn't respect parsing restriction */\n                }\n\n                if (length <= (size_t)(lowPrefix-match)) {\n                    /* match fits entirely within external dictionary : just copy */\n                    LZ4_memmove(op, dictEnd - (lowPrefix-match), length);\n                    op += length;\n                } else {\n                    /* match stretches into both external dictionary and current block */\n                    size_t const copySize = (size_t)(lowPrefix - match);\n                    size_t const restSize = length - copySize;\n                    LZ4_memcpy(op, dictEnd - copySize, copySize);\n                    op += copySize;\n                    if (restSize > (size_t)(op - lowPrefix)) {  /* overlap copy */\n                        BYTE* const endOfMatch = op + restSize;\n                        const BYTE* copyFrom = lowPrefix;\n                        while (op < endOfMatch) *op++ = *copyFrom++;\n                    } else {\n                        LZ4_memcpy(op, lowPrefix, restSize);\n                        op += restSize;\n                }   }\n                continue;\n            }\n            assert(match >= lowPrefix);\n\n            /* copy match within block */\n            cpy = op + length;\n\n            /* partialDecoding : may end anywhere within the block */\n            assert(op<=oend);\n            if (partialDecoding && (cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {\n                size_t const mlen = MIN(length, (size_t)(oend-op));\n                const BYTE* const matchEnd = match + mlen;\n                BYTE* const copyEnd = op + mlen;\n                if (matchEnd > op) {   /* overlap copy */\n                    while (op < copyEnd) { *op++ = *match++; }\n                } else {\n                    LZ4_memcpy(op, match, mlen);\n                }\n                op = copyEnd;\n                if (op == oend) { break; }\n                continue;\n            }\n\n            if (unlikely(offset<8)) {\n                LZ4_write32(op, 0);   /* silence msan warning when offset==0 */\n                op[0] = match[0];\n                op[1] = match[1];\n                op[2] = match[2];\n                op[3] = match[3];\n                match += inc32table[offset];\n                LZ4_memcpy(op+4, match, 4);\n                match -= dec64table[offset];\n            } else {\n                LZ4_memcpy(op, match, 8);\n                match += 8;\n            }\n            op += 8;\n\n            if (unlikely(cpy > oend-MATCH_SAFEGUARD_DISTANCE)) {\n                BYTE* const oCopyLimit = oend - (WILDCOPYLENGTH-1);\n                if (cpy > oend-LASTLITERALS) { goto _output_error; } /* Error : last LASTLITERALS bytes must be literals (uncompressed) */\n                if (op < oCopyLimit) {\n                    LZ4_wildCopy8(op, match, oCopyLimit);\n                    match += oCopyLimit - op;\n                    op = oCopyLimit;\n                }\n                while (op < cpy) { *op++ = *match++; }\n            } else {\n                LZ4_memcpy(op, match, 8);\n                if (length > 16)  { LZ4_wildCopy8(op+8, match+8, cpy); }\n            }\n            op = cpy;   /* wildcopy correction */\n        }\n\n        /* end of decoding */\n        DEBUGLOG(5, \"decoded %i bytes\", (int) (((char*)op)-dst));\n        return (int) (((char*)op)-dst);     /* Nb of output bytes decoded */\n\n        /* Overflow error detected */\n    _output_error:\n        return (int) (-(((const char*)ip)-src))-1;\n    }\n}\n\n\n/*===== Instantiate the API decoding functions. =====*/\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize,\n                                  decode_full_block, noDict,\n                                  (BYTE*)dest, NULL, 0);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, int targetOutputSize, int dstCapacity)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity,\n                                  partial_decode,\n                                  noDict, (BYTE*)dst, NULL, 0);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_fast(const char* source, char* dest, int originalSize)\n{\n    DEBUGLOG(5, \"LZ4_decompress_fast\");\n    return LZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                0, NULL, 0);\n}\n\n/*===== Instantiate a few more decoding cases, used more than once. =====*/\n\nLZ4_FORCE_O2 /* Exported, an obsolete API function. */\nint LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, withPrefix64k,\n                                  (BYTE*)dest - 64 KB, NULL, 0);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, withPrefix64k,\n                                  (BYTE*)dest - 64 KB, NULL, 0);\n}\n\n/* Another obsolete API function, paired with the previous one. */\nint LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize)\n{\n    return LZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                64 KB, NULL, 0);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, int compressedSize, int maxOutputSize,\n                                               size_t prefixSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, noDict,\n                                  (BYTE*)dest-prefixSize, NULL, 0);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity,\n                                               size_t prefixSize)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, noDict,\n                                  (BYTE*)dest-prefixSize, NULL, 0);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int maxOutputSize,\n                                     const void* dictStart, size_t dictSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, usingExtDict,\n                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);\n}\n\nLZ4_FORCE_O2\nint LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest,\n                                     int compressedSize, int targetOutputSize, int dstCapacity,\n                                     const void* dictStart, size_t dictSize)\n{\n    dstCapacity = MIN(targetOutputSize, dstCapacity);\n    return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity,\n                                  partial_decode, usingExtDict,\n                                  (BYTE*)dest, (const BYTE*)dictStart, dictSize);\n}\n\nLZ4_FORCE_O2\nstatic int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize,\n                                       const void* dictStart, size_t dictSize)\n{\n    return LZ4_decompress_unsafe_generic(\n                (const BYTE*)source, (BYTE*)dest, originalSize,\n                0, (const BYTE*)dictStart, dictSize);\n}\n\n/* The \"double dictionary\" mode, for use with e.g. ring buffers: the first part\n * of the dictionary is passed as prefix, and the second via dictStart + dictSize.\n * These routines are used only once, in LZ4_decompress_*_continue().\n */\nLZ4_FORCE_INLINE\nint LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compressedSize, int maxOutputSize,\n                                   size_t prefixSize, const void* dictStart, size_t dictSize)\n{\n    return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize,\n                                  decode_full_block, usingExtDict,\n                                  (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize);\n}\n\n/*===== streaming decompression functions =====*/\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4_streamDecode_t* LZ4_createStreamDecode(void)\n{\n    LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal));\n    return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t));\n}\n\nint LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream)\n{\n    if (LZ4_stream == NULL) { return 0; }  /* support free on NULL */\n    FREEMEM(LZ4_stream);\n    return 0;\n}\n#endif\n\n/*! LZ4_setStreamDecode() :\n *  Use this function to instruct where to find the dictionary.\n *  This function is not necessary if previous data is still available where it was decoded.\n *  Loading a size of 0 is allowed (same effect as no dictionary).\n * @return : 1 if OK, 0 if error\n */\nint LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize)\n{\n    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;\n    lz4sd->prefixSize = (size_t)dictSize;\n    if (dictSize) {\n        assert(dictionary != NULL);\n        lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize;\n    } else {\n        lz4sd->prefixEnd = (const BYTE*) dictionary;\n    }\n    lz4sd->externalDict = NULL;\n    lz4sd->extDictSize  = 0;\n    return 1;\n}\n\n/*! LZ4_decoderRingBufferSize() :\n *  when setting a ring buffer for streaming decompression (optional scenario),\n *  provides the minimum size of this ring buffer\n *  to be compatible with any source respecting maxBlockSize condition.\n *  Note : in a ring buffer scenario,\n *  blocks are presumed decompressed next to each other.\n *  When not enough space remains for next block (remainingSize < maxBlockSize),\n *  decoding resumes from beginning of ring buffer.\n * @return : minimum ring buffer size,\n *           or 0 if there is an error (invalid maxBlockSize).\n */\nint LZ4_decoderRingBufferSize(int maxBlockSize)\n{\n    if (maxBlockSize < 0) return 0;\n    if (maxBlockSize > LZ4_MAX_INPUT_SIZE) return 0;\n    if (maxBlockSize < 16) maxBlockSize = 16;\n    return LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize);\n}\n\n/*\n*_continue() :\n    These decoding functions allow decompression of multiple blocks in \"streaming\" mode.\n    Previously decoded blocks must still be available at the memory position where they were decoded.\n    If it's not possible, save the relevant part of decoded data into a safe buffer,\n    and indicate where it stands using LZ4_setStreamDecode()\n*/\nLZ4_FORCE_O2\nint LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int compressedSize, int maxOutputSize)\n{\n    LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse;\n    int result;\n\n    if (lz4sd->prefixSize == 0) {\n        /* The first call, no dictionary yet. */\n        assert(lz4sd->extDictSize == 0);\n        result = LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)result;\n        lz4sd->prefixEnd = (BYTE*)dest + result;\n    } else if (lz4sd->prefixEnd == (BYTE*)dest) {\n        /* They're rolling the current segment. */\n        if (lz4sd->prefixSize >= 64 KB - 1)\n            result = LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);\n        else if (lz4sd->extDictSize == 0)\n            result = LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize,\n                                                         lz4sd->prefixSize);\n        else\n            result = LZ4_decompress_safe_doubleDict(source, dest, compressedSize, maxOutputSize,\n                                                    lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize += (size_t)result;\n        lz4sd->prefixEnd  += result;\n    } else {\n        /* The buffer wraps around, or they're switching to another buffer. */\n        lz4sd->extDictSize = lz4sd->prefixSize;\n        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;\n        result = LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize,\n                                                  lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)result;\n        lz4sd->prefixEnd  = (BYTE*)dest + result;\n    }\n\n    return result;\n}\n\nLZ4_FORCE_O2 int\nLZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode,\n                        const char* source, char* dest, int originalSize)\n{\n    LZ4_streamDecode_t_internal* const lz4sd =\n        (assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse);\n    int result;\n\n    DEBUGLOG(5, \"LZ4_decompress_fast_continue (toDecodeSize=%i)\", originalSize);\n    assert(originalSize >= 0);\n\n    if (lz4sd->prefixSize == 0) {\n        DEBUGLOG(5, \"first invocation : no prefix nor extDict\");\n        assert(lz4sd->extDictSize == 0);\n        result = LZ4_decompress_fast(source, dest, originalSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)originalSize;\n        lz4sd->prefixEnd = (BYTE*)dest + originalSize;\n    } else if (lz4sd->prefixEnd == (BYTE*)dest) {\n        DEBUGLOG(5, \"continue using existing prefix\");\n        result = LZ4_decompress_unsafe_generic(\n                        (const BYTE*)source, (BYTE*)dest, originalSize,\n                        lz4sd->prefixSize,\n                        lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize += (size_t)originalSize;\n        lz4sd->prefixEnd  += originalSize;\n    } else {\n        DEBUGLOG(5, \"prefix becomes extDict\");\n        lz4sd->extDictSize = lz4sd->prefixSize;\n        lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize;\n        result = LZ4_decompress_fast_extDict(source, dest, originalSize,\n                                             lz4sd->externalDict, lz4sd->extDictSize);\n        if (result <= 0) return result;\n        lz4sd->prefixSize = (size_t)originalSize;\n        lz4sd->prefixEnd  = (BYTE*)dest + originalSize;\n    }\n\n    return result;\n}\n\n\n/*\nAdvanced decoding functions :\n*_usingDict() :\n    These decoding functions work the same as \"_continue\" ones,\n    the dictionary must be explicitly provided within parameters\n*/\n\nint LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const char* dictStart, int dictSize)\n{\n    if (dictSize==0)\n        return LZ4_decompress_safe(source, dest, compressedSize, maxOutputSize);\n    if (dictStart+dictSize == dest) {\n        if (dictSize >= 64 KB - 1) {\n            return LZ4_decompress_safe_withPrefix64k(source, dest, compressedSize, maxOutputSize);\n        }\n        assert(dictSize >= 0);\n        return LZ4_decompress_safe_withSmallPrefix(source, dest, compressedSize, maxOutputSize, (size_t)dictSize);\n    }\n    assert(dictSize >= 0);\n    return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize);\n}\n\nint LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize)\n{\n    if (dictSize==0)\n        return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity);\n    if (dictStart+dictSize == dest) {\n        if (dictSize >= 64 KB - 1) {\n            return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity);\n        }\n        assert(dictSize >= 0);\n        return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize);\n    }\n    assert(dictSize >= 0);\n    return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize);\n}\n\nint LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize)\n{\n    if (dictSize==0 || dictStart+dictSize == dest)\n        return LZ4_decompress_unsafe_generic(\n                        (const BYTE*)source, (BYTE*)dest, originalSize,\n                        (size_t)dictSize, NULL, 0);\n    assert(dictSize >= 0);\n    return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize);\n}\n\n\n/*=*************************************************\n*  Obsolete Functions\n***************************************************/\n/* obsolete compression functions */\nint LZ4_compress_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize)\n{\n    return LZ4_compress_default(source, dest, inputSize, maxOutputSize);\n}\nint LZ4_compress(const char* src, char* dest, int srcSize)\n{\n    return LZ4_compress_default(src, dest, srcSize, LZ4_compressBound(srcSize));\n}\nint LZ4_compress_limitedOutput_withState (void* state, const char* src, char* dst, int srcSize, int dstSize)\n{\n    return LZ4_compress_fast_extState(state, src, dst, srcSize, dstSize, 1);\n}\nint LZ4_compress_withState (void* state, const char* src, char* dst, int srcSize)\n{\n    return LZ4_compress_fast_extState(state, src, dst, srcSize, LZ4_compressBound(srcSize), 1);\n}\nint LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_stream, const char* src, char* dst, int srcSize, int dstCapacity)\n{\n    return LZ4_compress_fast_continue(LZ4_stream, src, dst, srcSize, dstCapacity, 1);\n}\nint LZ4_compress_continue (LZ4_stream_t* LZ4_stream, const char* source, char* dest, int inputSize)\n{\n    return LZ4_compress_fast_continue(LZ4_stream, source, dest, inputSize, LZ4_compressBound(inputSize), 1);\n}\n\n/*\nThese decompression functions are deprecated and should no longer be used.\nThey are only provided here for compatibility with older user programs.\n- LZ4_uncompress is totally equivalent to LZ4_decompress_fast\n- LZ4_uncompress_unknownOutputSize is totally equivalent to LZ4_decompress_safe\n*/\nint LZ4_uncompress (const char* source, char* dest, int outputSize)\n{\n    return LZ4_decompress_fast(source, dest, outputSize);\n}\nint LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize)\n{\n    return LZ4_decompress_safe(source, dest, isize, maxOutputSize);\n}\n\n/* Obsolete Streaming functions */\n\nint LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); }\n\nint LZ4_resetStreamState(void* state, char* inputBuffer)\n{\n    (void)inputBuffer;\n    LZ4_resetStream((LZ4_stream_t*)state);\n    return 0;\n}\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nvoid* LZ4_create (char* inputBuffer)\n{\n    (void)inputBuffer;\n    return LZ4_createStream();\n}\n#endif\n\nchar* LZ4_slideInputBuffer (void* state)\n{\n    /* avoid const char * -> char * conversion warning */\n    return (char *)(uptrval)((LZ4_stream_t*)state)->internal_donotuse.dictionary;\n}\n\n#endif   /* LZ4_COMMONDEFS_ONLY */\n"
  },
  {
    "path": "third-party/l4z/lz4.h",
    "content": "/*\n *  LZ4 - Fast LZ compression algorithm\n *  Header File\n *  Copyright (C) 2011-2020, Yann Collet.\n\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\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\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   You can contact the author at :\n    - LZ4 homepage : http://www.lz4.org\n    - LZ4 source repository : https://github.com/lz4/lz4\n*/\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\n#ifndef LZ4_H_2983827168210\n#define LZ4_H_2983827168210\n\n/* --- Dependency --- */\n#include <stddef.h>   /* size_t */\n\n\n/**\n  Introduction\n\n  LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,\n  scalable with multi-cores CPU. It features an extremely fast decoder, with speed in\n  multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.\n\n  The LZ4 compression library provides in-memory compression and decompression functions.\n  It gives full buffer control to user.\n  Compression can be done in:\n    - a single step (described as Simple Functions)\n    - a single step, reusing a context (described in Advanced Functions)\n    - unbounded multiple steps (described as Streaming compression)\n\n  lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md).\n  Decompressing such a compressed block requires additional metadata.\n  Exact metadata depends on exact decompression function.\n  For the typical case of LZ4_decompress_safe(),\n  metadata includes block's compressed size, and maximum bound of decompressed size.\n  Each application is free to encode and pass such metadata in whichever way it wants.\n\n  lz4.h only handle blocks, it can not generate Frames.\n\n  Blocks are different from Frames (doc/lz4_Frame_format.md).\n  Frames bundle both blocks and metadata in a specified manner.\n  Embedding metadata is required for compressed data to be self-contained and portable.\n  Frame format is delivered through a companion API, declared in lz4frame.h.\n  The `lz4` CLI can only manage frames.\n*/\n\n/*^***************************************************************\n*  Export parameters\n*****************************************************************/\n/*\n*  LZ4_DLL_EXPORT :\n*  Enable exporting of functions when building a Windows DLL\n*  LZ4LIB_VISIBILITY :\n*  Control library symbols visibility.\n*/\n#ifndef LZ4LIB_VISIBILITY\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define LZ4LIB_VISIBILITY __attribute__ ((visibility (\"default\")))\n#  else\n#    define LZ4LIB_VISIBILITY\n#  endif\n#endif\n#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)\n#  define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY\n#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)\n#  define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/\n#else\n#  define LZ4LIB_API LZ4LIB_VISIBILITY\n#endif\n\n/*! LZ4_FREESTANDING :\n *  When this macro is set to 1, it enables \"freestanding mode\" that is\n *  suitable for typical freestanding environment which doesn't support\n *  standard C library.\n *\n *  - LZ4_FREESTANDING is a compile-time switch.\n *  - It requires the following macros to be defined:\n *    LZ4_memcpy, LZ4_memmove, LZ4_memset.\n *  - It only enables LZ4/HC functions which don't use heap.\n *    All LZ4F_* functions are not supported.\n *  - See tests/freestanding.c to check its basic setup.\n */\n#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1)\n#  define LZ4_HEAPMODE 0\n#  define LZ4HC_HEAPMODE 0\n#  define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1\n#  if !defined(LZ4_memcpy)\n#    error \"LZ4_FREESTANDING requires macro 'LZ4_memcpy'.\"\n#  endif\n#  if !defined(LZ4_memset)\n#    error \"LZ4_FREESTANDING requires macro 'LZ4_memset'.\"\n#  endif\n#  if !defined(LZ4_memmove)\n#    error \"LZ4_FREESTANDING requires macro 'LZ4_memmove'.\"\n#  endif\n#elif ! defined(LZ4_FREESTANDING)\n#  define LZ4_FREESTANDING 0\n#endif\n\n\n/*------   Version   ------*/\n#define LZ4_VERSION_MAJOR    1    /* for breaking interface changes  */\n#define LZ4_VERSION_MINOR    9    /* for new (non-breaking) interface capabilities */\n#define LZ4_VERSION_RELEASE  4    /* for tweaks, bug-fixes, or development */\n\n#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)\n\n#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE\n#define LZ4_QUOTE(str) #str\n#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)\n#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)  /* requires v1.7.3+ */\n\nLZ4LIB_API int LZ4_versionNumber (void);  /**< library version number; useful to check dll version; requires v1.3.0+ */\nLZ4LIB_API const char* LZ4_versionString (void);   /**< library version string; useful to check dll version; requires v1.7.5+ */\n\n\n/*-************************************\n*  Tuning parameter\n**************************************/\n#define LZ4_MEMORY_USAGE_MIN 10\n#define LZ4_MEMORY_USAGE_DEFAULT 14\n#define LZ4_MEMORY_USAGE_MAX 20\n\n/*!\n * LZ4_MEMORY_USAGE :\n * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; )\n * Increasing memory usage improves compression ratio, at the cost of speed.\n * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.\n * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache\n */\n#ifndef LZ4_MEMORY_USAGE\n# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT\n#endif\n\n#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)\n#  error \"LZ4_MEMORY_USAGE is too small !\"\n#endif\n\n#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX)\n#  error \"LZ4_MEMORY_USAGE is too large !\"\n#endif\n\n/*-************************************\n*  Simple Functions\n**************************************/\n/*! LZ4_compress_default() :\n *  Compresses 'srcSize' bytes from buffer 'src'\n *  into already allocated 'dst' buffer of size 'dstCapacity'.\n *  Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).\n *  It also runs faster, so it's a recommended setting.\n *  If the function cannot compress 'src' into a more limited 'dst' budget,\n *  compression stops *immediately*, and the function result is zero.\n *  In which case, 'dst' content is undefined (invalid).\n *      srcSize : max supported value is LZ4_MAX_INPUT_SIZE.\n *      dstCapacity : size of buffer 'dst' (which must be already allocated)\n *     @return  : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)\n *                or 0 if compression fails\n * Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).\n */\nLZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);\n\n/*! LZ4_decompress_safe() :\n *  compressedSize : is the exact complete size of the compressed block.\n *  dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size.\n * @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)\n *           If destination buffer is not large enough, decoding will stop and output an error code (negative value).\n *           If the source stream is detected malformed, the function will stop decoding and return a negative result.\n * Note 1 : This function is protected against malicious data packets :\n *          it will never writes outside 'dst' buffer, nor read outside 'source' buffer,\n *          even if the compressed block is maliciously modified to order the decoder to do these actions.\n *          In such case, the decoder stops immediately, and considers the compressed block malformed.\n * Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.\n *          The implementation is free to send / store / derive this information in whichever way is most beneficial.\n *          If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.\n */\nLZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);\n\n\n/*-************************************\n*  Advanced Functions\n**************************************/\n#define LZ4_MAX_INPUT_SIZE        0x7E000000   /* 2 113 929 216 bytes */\n#define LZ4_COMPRESSBOUND(isize)  ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)\n\n/*! LZ4_compressBound() :\n    Provides the maximum size that LZ4 compression may output in a \"worst case\" scenario (input data not compressible)\n    This function is primarily useful for memory allocation purposes (destination buffer size).\n    Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).\n    Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)\n        inputSize  : max supported value is LZ4_MAX_INPUT_SIZE\n        return : maximum output size in a \"worst case\" scenario\n              or 0, if input size is incorrect (too large or negative)\n*/\nLZ4LIB_API int LZ4_compressBound(int inputSize);\n\n/*! LZ4_compress_fast() :\n    Same as LZ4_compress_default(), but allows selection of \"acceleration\" factor.\n    The larger the acceleration value, the faster the algorithm, but also the lesser the compression.\n    It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.\n    An acceleration value of \"1\" is the same as regular LZ4_compress_default()\n    Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).\n    Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c).\n*/\nLZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n\n/*! LZ4_compress_fast_extState() :\n *  Same as LZ4_compress_fast(), using an externally allocated memory space for its state.\n *  Use LZ4_sizeofState() to know how much memory must be allocated,\n *  and allocate it on 8-bytes boundaries (using `malloc()` typically).\n *  Then, provide this buffer as `void* state` to compression function.\n */\nLZ4LIB_API int LZ4_sizeofState(void);\nLZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n\n/*! LZ4_compress_destSize() :\n *  Reverse the logic : compresses as much data as possible from 'src' buffer\n *  into already allocated buffer 'dst', of size >= 'targetDestSize'.\n *  This function either compresses the entire 'src' content into 'dst' if it's large enough,\n *  or fill 'dst' buffer completely with as much data as possible from 'src'.\n *  note: acceleration parameter is fixed to \"default\".\n *\n * *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.\n *               New value is necessarily <= input value.\n * @return : Nb bytes written into 'dst' (necessarily <= targetDestSize)\n *           or 0 if compression fails.\n *\n * Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+):\n *        the produced compressed content could, in specific circumstances,\n *        require to be decompressed into a destination buffer larger\n *        by at least 1 byte than the content to decompress.\n *        If an application uses `LZ4_compress_destSize()`,\n *        it's highly recommended to update liblz4 to v1.9.2 or better.\n *        If this can't be done or ensured,\n *        the receiving decompression function should provide\n *        a dstCapacity which is > decompressedSize, by at least 1 byte.\n *        See https://github.com/lz4/lz4/issues/859 for details\n */\nLZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);\n\n\n/*! LZ4_decompress_safe_partial() :\n *  Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',\n *  into destination buffer 'dst' of size 'dstCapacity'.\n *  Up to 'targetOutputSize' bytes will be decoded.\n *  The function stops decoding on reaching this objective.\n *  This can be useful to boost performance\n *  whenever only the beginning of a block is required.\n *\n * @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize)\n *           If source stream is detected malformed, function returns a negative result.\n *\n *  Note 1 : @return can be < targetOutputSize, if compressed block contains less data.\n *\n *  Note 2 : targetOutputSize must be <= dstCapacity\n *\n *  Note 3 : this function effectively stops decoding on reaching targetOutputSize,\n *           so dstCapacity is kind of redundant.\n *           This is because in older versions of this function,\n *           decoding operation would still write complete sequences.\n *           Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize,\n *           it could write more bytes, though only up to dstCapacity.\n *           Some \"margin\" used to be required for this operation to work properly.\n *           Thankfully, this is no longer necessary.\n *           The function nonetheless keeps the same signature, in an effort to preserve API compatibility.\n *\n *  Note 4 : If srcSize is the exact size of the block,\n *           then targetOutputSize can be any value,\n *           including larger than the block's decompressed size.\n *           The function will, at most, generate block's decompressed size.\n *\n *  Note 5 : If srcSize is _larger_ than block's compressed size,\n *           then targetOutputSize **MUST** be <= block's decompressed size.\n *           Otherwise, *silent corruption will occur*.\n */\nLZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);\n\n\n/*-*********************************************\n*  Streaming Compression Functions\n***********************************************/\ntypedef union LZ4_stream_u LZ4_stream_t;  /* incomplete type (defined later) */\n\n/**\n Note about RC_INVOKED\n\n - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).\n   https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros\n\n - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)\n   and reports warning \"RC4011: identifier truncated\".\n\n - To eliminate the warning, we surround long preprocessor symbol with\n   \"#if !defined(RC_INVOKED) ... #endif\" block that means\n   \"skip this block when rc.exe is trying to read it\".\n*/\n#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4LIB_API LZ4_stream_t* LZ4_createStream(void);\nLZ4LIB_API int           LZ4_freeStream (LZ4_stream_t* streamPtr);\n#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */\n#endif\n\n/*! LZ4_resetStream_fast() : v1.9.0+\n *  Use this to prepare an LZ4_stream_t for a new chain of dependent blocks\n *  (e.g., LZ4_compress_fast_continue()).\n *\n *  An LZ4_stream_t must be initialized once before usage.\n *  This is automatically done when created by LZ4_createStream().\n *  However, should the LZ4_stream_t be simply declared on stack (for example),\n *  it's necessary to initialize it first, using LZ4_initStream().\n *\n *  After init, start any new stream with LZ4_resetStream_fast().\n *  A same LZ4_stream_t can be re-used multiple times consecutively\n *  and compress multiple streams,\n *  provided that it starts each new stream with LZ4_resetStream_fast().\n *\n *  LZ4_resetStream_fast() is much faster than LZ4_initStream(),\n *  but is not compatible with memory regions containing garbage data.\n *\n *  Note: it's only useful to call LZ4_resetStream_fast()\n *        in the context of streaming compression.\n *        The *extState* functions perform their own resets.\n *        Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive.\n */\nLZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);\n\n/*! LZ4_loadDict() :\n *  Use this function to reference a static dictionary into LZ4_stream_t.\n *  The dictionary must remain available during compression.\n *  LZ4_loadDict() triggers a reset, so any previous data will be forgotten.\n *  The same dictionary will have to be loaded on decompression side for successful decoding.\n *  Dictionary are useful for better compression of small data (KB range).\n *  While LZ4 accept any input as dictionary,\n *  results are generally better when using Zstandard's Dictionary Builder.\n *  Loading a size of 0 is allowed, and is the same as reset.\n * @return : loaded dictionary size, in bytes (necessarily <= 64 KB)\n */\nLZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);\n\n/*! LZ4_compress_fast_continue() :\n *  Compress 'src' content using data from previously compressed blocks, for better compression ratio.\n * 'dst' buffer must be already allocated.\n *  If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.\n *\n * @return : size of compressed block\n *           or 0 if there is an error (typically, cannot fit into 'dst').\n *\n *  Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.\n *           Each block has precise boundaries.\n *           Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata.\n *           It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.\n *\n *  Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory !\n *\n *  Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB.\n *           Make sure that buffers are separated, by at least one byte.\n *           This construction ensures that each block only depends on previous block.\n *\n *  Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.\n *\n *  Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed.\n */\nLZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n/*! LZ4_saveDict() :\n *  If last 64KB data cannot be guaranteed to remain available at its current memory location,\n *  save it into a safer place (char* safeBuffer).\n *  This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),\n *  but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.\n * @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.\n */\nLZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);\n\n\n/*-**********************************************\n*  Streaming Decompression Functions\n*  Bufferless synchronous API\n************************************************/\ntypedef union LZ4_streamDecode_u LZ4_streamDecode_t;   /* tracking context */\n\n/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :\n *  creation / destruction of streaming decompression tracking context.\n *  A tracking context can be re-used multiple times.\n */\n#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);\nLZ4LIB_API int                 LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);\n#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */\n#endif\n\n/*! LZ4_setStreamDecode() :\n *  An LZ4_streamDecode_t context can be allocated once and re-used multiple times.\n *  Use this function to start decompression of a new stream of blocks.\n *  A dictionary can optionally be set. Use NULL or size 0 for a reset order.\n *  Dictionary is presumed stable : it must remain accessible and unmodified during next decompression.\n * @return : 1 if OK, 0 if error\n */\nLZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);\n\n/*! LZ4_decoderRingBufferSize() : v1.8.2+\n *  Note : in a ring buffer scenario (optional),\n *  blocks are presumed decompressed next to each other\n *  up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize),\n *  at which stage it resumes from beginning of ring buffer.\n *  When setting such a ring buffer for streaming decompression,\n *  provides the minimum size of this ring buffer\n *  to be compatible with any source respecting maxBlockSize condition.\n * @return : minimum ring buffer size,\n *           or 0 if there is an error (invalid maxBlockSize).\n */\nLZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);\n#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize))  /* for static allocation; maxBlockSize presumed valid */\n\n/*! LZ4_decompress_*_continue() :\n *  These decoding functions allow decompression of consecutive blocks in \"streaming\" mode.\n *  A block is an unsplittable entity, it must be presented entirely to a decompression function.\n *  Decompression functions only accepts one block at a time.\n *  The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded.\n *  If less than 64KB of data has been decoded, all the data must be present.\n *\n *  Special : if decompression side sets a ring buffer, it must respect one of the following conditions :\n *  - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize).\n *    maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes.\n *    In which case, encoding and decoding buffers do not need to be synchronized.\n *    Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize.\n *  - Synchronized mode :\n *    Decompression buffer size is _exactly_ the same as compression buffer size,\n *    and follows exactly same update rule (block boundaries at same positions),\n *    and decoding function is provided with exact decompressed size of each block (exception for last block of the stream),\n *    _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB).\n *  - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes.\n *    In which case, encoding and decoding buffers do not need to be synchronized,\n *    and encoding ring buffer can have any size, including small ones ( < 64 KB).\n *\n *  Whenever these conditions are not possible,\n *  save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,\n *  then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.\n*/\nLZ4LIB_API int\nLZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,\n                        const char* src, char* dst,\n                        int srcSize, int dstCapacity);\n\n\n/*! LZ4_decompress_*_usingDict() :\n *  These decoding functions work the same as\n *  a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue()\n *  They are stand-alone, and don't need an LZ4_streamDecode_t structure.\n *  Dictionary is presumed stable : it must remain accessible and unmodified during decompression.\n *  Performance tip : Decompression speed can be substantially increased\n *                    when dst == dictStart + dictSize.\n */\nLZ4LIB_API int\nLZ4_decompress_safe_usingDict(const char* src, char* dst,\n                              int srcSize, int dstCapacity,\n                              const char* dictStart, int dictSize);\n\nLZ4LIB_API int\nLZ4_decompress_safe_partial_usingDict(const char* src, char* dst,\n                                      int compressedSize,\n                                      int targetOutputSize, int maxOutputSize,\n                                      const char* dictStart, int dictSize);\n\n#endif /* LZ4_H_2983827168210 */\n\n\n/*^*************************************\n * !!!!!!   STATIC LINKING ONLY   !!!!!!\n ***************************************/\n\n/*-****************************************************************************\n * Experimental section\n *\n * Symbols declared in this section must be considered unstable. Their\n * signatures or semantics may change, or they may be removed altogether in the\n * future. They are therefore only safe to depend on when the caller is\n * statically linked against the library.\n *\n * To protect against unsafe usage, not only are the declarations guarded,\n * the definitions are hidden by default\n * when building LZ4 as a shared/dynamic library.\n *\n * In order to access these declarations,\n * define LZ4_STATIC_LINKING_ONLY in your application\n * before including LZ4's headers.\n *\n * In order to make their implementations accessible dynamically, you must\n * define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library.\n ******************************************************************************/\n\n#ifdef LZ4_STATIC_LINKING_ONLY\n\n#ifndef LZ4_STATIC_3504398509\n#define LZ4_STATIC_3504398509\n\n#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS\n#define LZ4LIB_STATIC_API LZ4LIB_API\n#else\n#define LZ4LIB_STATIC_API\n#endif\n\n\n/*! LZ4_compress_fast_extState_fastReset() :\n *  A variant of LZ4_compress_fast_extState().\n *\n *  Using this variant avoids an expensive initialization step.\n *  It is only safe to call if the state buffer is known to be correctly initialized already\n *  (see above comment on LZ4_resetStream_fast() for a definition of \"correctly initialized\").\n *  From a high level, the difference is that\n *  this function initializes the provided state with a call to something like LZ4_resetStream_fast()\n *  while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().\n */\nLZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);\n\n/*! LZ4_attach_dictionary() :\n *  This is an experimental API that allows\n *  efficient use of a static dictionary many times.\n *\n *  Rather than re-loading the dictionary buffer into a working context before\n *  each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a\n *  working LZ4_stream_t, this function introduces a no-copy setup mechanism,\n *  in which the working stream references the dictionary stream in-place.\n *\n *  Several assumptions are made about the state of the dictionary stream.\n *  Currently, only streams which have been prepared by LZ4_loadDict() should\n *  be expected to work.\n *\n *  Alternatively, the provided dictionaryStream may be NULL,\n *  in which case any existing dictionary stream is unset.\n *\n *  If a dictionary is provided, it replaces any pre-existing stream history.\n *  The dictionary contents are the only history that can be referenced and\n *  logically immediately precede the data compressed in the first subsequent\n *  compression call.\n *\n *  The dictionary will only remain attached to the working stream through the\n *  first compression call, at the end of which it is cleared. The dictionary\n *  stream (and source buffer) must remain in-place / accessible / unchanged\n *  through the completion of the first compression call on the stream.\n */\nLZ4LIB_STATIC_API void\nLZ4_attach_dictionary(LZ4_stream_t* workingStream,\n                const LZ4_stream_t* dictionaryStream);\n\n\n/*! In-place compression and decompression\n *\n * It's possible to have input and output sharing the same buffer,\n * for highly constrained memory environments.\n * In both cases, it requires input to lay at the end of the buffer,\n * and decompression to start at beginning of the buffer.\n * Buffer size must feature some margin, hence be larger than final size.\n *\n * |<------------------------buffer--------------------------------->|\n *                             |<-----------compressed data--------->|\n * |<-----------decompressed size------------------>|\n *                                                  |<----margin---->|\n *\n * This technique is more useful for decompression,\n * since decompressed size is typically larger,\n * and margin is short.\n *\n * In-place decompression will work inside any buffer\n * which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).\n * This presumes that decompressedSize > compressedSize.\n * Otherwise, it means compression actually expanded data,\n * and it would be more efficient to store such data with a flag indicating it's not compressed.\n * This can happen when data is not compressible (already compressed, or encrypted).\n *\n * For in-place compression, margin is larger, as it must be able to cope with both\n * history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX,\n * and data expansion, which can happen when input is not compressible.\n * As a consequence, buffer size requirements are much higher,\n * and memory savings offered by in-place compression are more limited.\n *\n * There are ways to limit this cost for compression :\n * - Reduce history size, by modifying LZ4_DISTANCE_MAX.\n *   Note that it is a compile-time constant, so all compressions will apply this limit.\n *   Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX,\n *   so it's a reasonable trick when inputs are known to be small.\n * - Require the compressor to deliver a \"maximum compressed size\".\n *   This is the `dstCapacity` parameter in `LZ4_compress*()`.\n *   When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail,\n *   in which case, the return code will be 0 (zero).\n *   The caller must be ready for these cases to happen,\n *   and typically design a backup scheme to send data uncompressed.\n * The combination of both techniques can significantly reduce\n * the amount of margin required for in-place compression.\n *\n * In-place compression can work in any buffer\n * which size is >= (maxCompressedSize)\n * with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.\n * LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX,\n * so it's possible to reduce memory requirements by playing with them.\n */\n\n#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize)          (((compressedSize) >> 8) + 32)\n#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize)   ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize))  /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */\n\n#ifndef LZ4_DISTANCE_MAX   /* history window size; can be user-defined at compile time */\n#  define LZ4_DISTANCE_MAX 65535   /* set to maximum value by default */\n#endif\n\n#define LZ4_COMPRESS_INPLACE_MARGIN                           (LZ4_DISTANCE_MAX + 32)   /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */\n#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize)   ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN)  /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */\n\n#endif   /* LZ4_STATIC_3504398509 */\n#endif   /* LZ4_STATIC_LINKING_ONLY */\n\n\n\n#ifndef LZ4_H_98237428734687\n#define LZ4_H_98237428734687\n\n/*-************************************************************\n *  Private Definitions\n **************************************************************\n * Do not use these definitions directly.\n * They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.\n * Accessing members will expose user code to API and/or ABI break in future versions of the library.\n **************************************************************/\n#define LZ4_HASHLOG   (LZ4_MEMORY_USAGE-2)\n#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)\n#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG)       /* required as macro for static allocation */\n\n#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)\n# include <stdint.h>\n  typedef  int8_t  LZ4_i8;\n  typedef uint8_t  LZ4_byte;\n  typedef uint16_t LZ4_u16;\n  typedef uint32_t LZ4_u32;\n#else\n  typedef   signed char  LZ4_i8;\n  typedef unsigned char  LZ4_byte;\n  typedef unsigned short LZ4_u16;\n  typedef unsigned int   LZ4_u32;\n#endif\n\n/*! LZ4_stream_t :\n *  Never ever use below internal definitions directly !\n *  These definitions are not API/ABI safe, and may change in future versions.\n *  If you need static allocation, declare or allocate an LZ4_stream_t object.\n**/\n\ntypedef struct LZ4_stream_t_internal LZ4_stream_t_internal;\nstruct LZ4_stream_t_internal {\n    LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];\n    const LZ4_byte* dictionary;\n    const LZ4_stream_t_internal* dictCtx;\n    LZ4_u32 currentOffset;\n    LZ4_u32 tableType;\n    LZ4_u32 dictSize;\n    /* Implicit padding to ensure structure is aligned */\n};\n\n#define LZ4_STREAM_MINSIZE  ((1UL << LZ4_MEMORY_USAGE) + 32)  /* static size, for inter-version compatibility */\nunion LZ4_stream_u {\n    char minStateSize[LZ4_STREAM_MINSIZE];\n    LZ4_stream_t_internal internal_donotuse;\n}; /* previously typedef'd to LZ4_stream_t */\n\n\n/*! LZ4_initStream() : v1.9.0+\n *  An LZ4_stream_t structure must be initialized at least once.\n *  This is automatically done when invoking LZ4_createStream(),\n *  but it's not when the structure is simply declared on stack (for example).\n *\n *  Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t.\n *  It can also initialize any arbitrary buffer of sufficient size,\n *  and will @return a pointer of proper type upon initialization.\n *\n *  Note : initialization fails if size and alignment conditions are not respected.\n *         In which case, the function will @return NULL.\n *  Note2: An LZ4_stream_t structure guarantees correct alignment and size.\n *  Note3: Before v1.9.0, use LZ4_resetStream() instead\n**/\nLZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size);\n\n\n/*! LZ4_streamDecode_t :\n *  Never ever use below internal definitions directly !\n *  These definitions are not API/ABI safe, and may change in future versions.\n *  If you need static allocation, declare or allocate an LZ4_streamDecode_t object.\n**/\ntypedef struct {\n    const LZ4_byte* externalDict;\n    const LZ4_byte* prefixEnd;\n    size_t extDictSize;\n    size_t prefixSize;\n} LZ4_streamDecode_t_internal;\n\n#define LZ4_STREAMDECODE_MINSIZE 32\nunion LZ4_streamDecode_u {\n    char minStateSize[LZ4_STREAMDECODE_MINSIZE];\n    LZ4_streamDecode_t_internal internal_donotuse;\n} ;   /* previously typedef'd to LZ4_streamDecode_t */\n\n\n\n/*-************************************\n*  Obsolete Functions\n**************************************/\n\n/*! Deprecation warnings\n *\n *  Deprecated functions make the compiler generate a warning when invoked.\n *  This is meant to invite users to update their source code.\n *  Should deprecation warnings be a problem, it is generally possible to disable them,\n *  typically with -Wno-deprecated-declarations for gcc\n *  or _CRT_SECURE_NO_WARNINGS in Visual.\n *\n *  Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS\n *  before including the header file.\n */\n#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS\n#  define LZ4_DEPRECATED(message)   /* disable deprecation warnings */\n#else\n#  if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */\n#    define LZ4_DEPRECATED(message) [[deprecated(message)]]\n#  elif defined(_MSC_VER)\n#    define LZ4_DEPRECATED(message) __declspec(deprecated(message))\n#  elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))\n#    define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))\n#  elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)\n#    define LZ4_DEPRECATED(message) __attribute__((deprecated))\n#  else\n#    pragma message(\"WARNING: LZ4_DEPRECATED needs custom implementation for this compiler\")\n#    define LZ4_DEPRECATED(message)   /* disabled */\n#  endif\n#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */\n\n/*! Obsolete compression functions (since v1.7.3) */\nLZ4_DEPRECATED(\"use LZ4_compress_default() instead\")       LZ4LIB_API int LZ4_compress               (const char* src, char* dest, int srcSize);\nLZ4_DEPRECATED(\"use LZ4_compress_default() instead\")       LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_extState() instead\") LZ4LIB_API int LZ4_compress_withState               (void* state, const char* source, char* dest, int inputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_extState() instead\") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_continue() instead\") LZ4LIB_API int LZ4_compress_continue                (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_fast_continue() instead\") LZ4LIB_API int LZ4_compress_limitedOutput_continue  (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);\n\n/*! Obsolete decompression functions (since v1.8.0) */\nLZ4_DEPRECATED(\"use LZ4_decompress_fast() instead\") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);\nLZ4_DEPRECATED(\"use LZ4_decompress_safe() instead\") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);\n\n/* Obsolete streaming functions (since v1.7.0)\n * degraded functionality; do not use!\n *\n * In order to perform streaming compression, these functions depended on data\n * that is no longer tracked in the state. They have been preserved as well as\n * possible: using them will still produce a correct output. However, they don't\n * actually retain any history between compression calls. The compression ratio\n * achieved will therefore be no better than compressing each chunk\n * independently.\n */\nLZ4_DEPRECATED(\"Use LZ4_createStream() instead\") LZ4LIB_API void* LZ4_create (char* inputBuffer);\nLZ4_DEPRECATED(\"Use LZ4_createStream() instead\") LZ4LIB_API int   LZ4_sizeofStreamState(void);\nLZ4_DEPRECATED(\"Use LZ4_resetStream() instead\")  LZ4LIB_API int   LZ4_resetStreamState(void* state, char* inputBuffer);\nLZ4_DEPRECATED(\"Use LZ4_saveDict() instead\")     LZ4LIB_API char* LZ4_slideInputBuffer (void* state);\n\n/*! Obsolete streaming decoding functions (since v1.7.0) */\nLZ4_DEPRECATED(\"use LZ4_decompress_safe_usingDict() instead\") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);\nLZ4_DEPRECATED(\"use LZ4_decompress_fast_usingDict() instead\") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);\n\n/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) :\n *  These functions used to be faster than LZ4_decompress_safe(),\n *  but this is no longer the case. They are now slower.\n *  This is because LZ4_decompress_fast() doesn't know the input size,\n *  and therefore must progress more cautiously into the input buffer to not read beyond the end of block.\n *  On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.\n *  As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated.\n *\n *  The last remaining LZ4_decompress_fast() specificity is that\n *  it can decompress a block without knowing its compressed size.\n *  Such functionality can be achieved in a more secure manner\n *  by employing LZ4_decompress_safe_partial().\n *\n *  Parameters:\n *  originalSize : is the uncompressed size to regenerate.\n *                 `dst` must be already allocated, its size must be >= 'originalSize' bytes.\n * @return : number of bytes read from source buffer (== compressed size).\n *           The function expects to finish at block's end exactly.\n *           If the source stream is detected malformed, the function stops decoding and returns a negative result.\n *  note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer.\n *         However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds.\n *         Also, since match offsets are not validated, match reads from 'src' may underflow too.\n *         These issues never happen if input (compressed) data is correct.\n *         But they may happen if input data is invalid (error or intentional tampering).\n *         As a consequence, use these functions in trusted environments with trusted data **only**.\n */\nLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead\")\nLZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);\nLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead\")\nLZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);\nLZ4_DEPRECATED(\"This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead\")\nLZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);\n\n/*! LZ4_resetStream() :\n *  An LZ4_stream_t structure must be initialized at least once.\n *  This is done with LZ4_initStream(), or LZ4_resetStream().\n *  Consider switching to LZ4_initStream(),\n *  invoking LZ4_resetStream() will trigger deprecation warnings in the future.\n */\nLZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);\n\n\n#endif /* LZ4_H_98237428734687 */\n\n\n#if defined (__cplusplus)\n}\n#endif\n"
  },
  {
    "path": "third-party/l4z/lz4file.c",
    "content": "/*\n * LZ4 file library\n * Copyright (C) 2022, Xiaomi Inc.\n *\n * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\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 *\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 * You can contact the author at :\n * - LZ4 homepage : http://www.lz4.org\n * - LZ4 source repository : https://github.com/lz4/lz4\n */\n#include <stdlib.h>\n#include <string.h>\n#include \"lz4.h\"\n#include \"lz4file.h\"\n\nstruct LZ4_readFile_s {\n  LZ4F_dctx* dctxPtr;\n  FILE* fp;\n  LZ4_byte* srcBuf;\n  size_t srcBufNext;\n  size_t srcBufSize;\n  size_t srcBufMaxSize;\n};\n\nstruct LZ4_writeFile_s {\n  LZ4F_cctx* cctxPtr;\n  FILE* fp;\n  LZ4_byte* dstBuf;\n  size_t maxWriteSize;\n  size_t dstBufMaxSize;\n  LZ4F_errorCode_t errCode;\n};\n\nLZ4F_errorCode_t LZ4F_readOpen(LZ4_readFile_t** lz4fRead, FILE* fp)\n{\n  char buf[LZ4F_HEADER_SIZE_MAX];\n  size_t consumedSize;\n  LZ4F_errorCode_t ret;\n  LZ4F_frameInfo_t info;\n\n  if (fp == NULL || lz4fRead == NULL) {\n    return -LZ4F_ERROR_GENERIC;\n  }\n\n  *lz4fRead = (LZ4_readFile_t*)calloc(1, sizeof(LZ4_readFile_t));\n  if (*lz4fRead == NULL) {\n    return -LZ4F_ERROR_allocation_failed;\n  }\n\n  ret = LZ4F_createDecompressionContext(&(*lz4fRead)->dctxPtr, LZ4F_getVersion());\n  if (LZ4F_isError(ret)) {\n    free(*lz4fRead);\n    return ret;\n  }\n\n  (*lz4fRead)->fp = fp;\n  consumedSize = fread(buf, 1, sizeof(buf), (*lz4fRead)->fp);\n  if (consumedSize != sizeof(buf)) {\n    free(*lz4fRead);\n    return -LZ4F_ERROR_GENERIC;\n  }\n\n  ret = LZ4F_getFrameInfo((*lz4fRead)->dctxPtr, &info, buf, &consumedSize);\n  if (LZ4F_isError(ret)) {\n      LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr);\n      free(*lz4fRead);\n      return ret;\n    }\n\n  switch (info.blockSizeID) {\n    case LZ4F_default :\n    case LZ4F_max64KB :\n      (*lz4fRead)->srcBufMaxSize = 64 * 1024;\n      break;\n    case LZ4F_max256KB:\n      (*lz4fRead)->srcBufMaxSize = 256 * 1024;\n      break;\n    case LZ4F_max1MB:\n      (*lz4fRead)->srcBufMaxSize = 1 * 1024 * 1024;\n      break;\n    case LZ4F_max4MB:\n      (*lz4fRead)->srcBufMaxSize = 4 * 1024 * 1024;\n      break;\n    default:\n      LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr);\n      free(*lz4fRead);\n      return -LZ4F_ERROR_maxBlockSize_invalid;\n  }\n\n  (*lz4fRead)->srcBuf = (LZ4_byte*)malloc((*lz4fRead)->srcBufMaxSize);\n  if ((*lz4fRead)->srcBuf == NULL) {\n    LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr);\n    free(lz4fRead);\n    return -LZ4F_ERROR_allocation_failed;\n  }\n\n  (*lz4fRead)->srcBufSize = sizeof(buf) - consumedSize;\n  memcpy((*lz4fRead)->srcBuf, buf + consumedSize, (*lz4fRead)->srcBufSize);\n\n  return ret;\n}\n\nsize_t LZ4F_read(LZ4_readFile_t* lz4fRead, void* buf, size_t size)\n{\n  LZ4_byte* p = (LZ4_byte*)buf;\n  size_t next = 0;\n\n  if (lz4fRead == NULL || buf == NULL)\n    return -LZ4F_ERROR_GENERIC;\n\n  while (next < size) {\n    size_t srcsize = lz4fRead->srcBufSize - lz4fRead->srcBufNext;\n    size_t dstsize = size - next;\n    size_t ret;\n\n    if (srcsize == 0) {\n      ret = fread(lz4fRead->srcBuf, 1, lz4fRead->srcBufMaxSize, lz4fRead->fp);\n      if (ret > 0) {\n        lz4fRead->srcBufSize = ret;\n        srcsize = lz4fRead->srcBufSize;\n        lz4fRead->srcBufNext = 0;\n      }\n      else if (ret == 0) {\n        break;\n      }\n      else {\n        return -LZ4F_ERROR_GENERIC;\n      }\n    }\n\n    ret = LZ4F_decompress(lz4fRead->dctxPtr,\n                          p, &dstsize,\n                          lz4fRead->srcBuf + lz4fRead->srcBufNext,\n                          &srcsize,\n                          NULL);\n    if (LZ4F_isError(ret)) {\n        return ret;\n    }\n\n    lz4fRead->srcBufNext += srcsize;\n    next += dstsize;\n    p += dstsize;\n  }\n\n  return next;\n}\n\nLZ4F_errorCode_t LZ4F_readClose(LZ4_readFile_t* lz4fRead)\n{\n  if (lz4fRead == NULL)\n    return -LZ4F_ERROR_GENERIC;\n  LZ4F_freeDecompressionContext(lz4fRead->dctxPtr);\n  free(lz4fRead->srcBuf);\n  free(lz4fRead);\n  return LZ4F_OK_NoError;\n}\n\nLZ4F_errorCode_t LZ4F_writeOpen(LZ4_writeFile_t** lz4fWrite, FILE* fp, const LZ4F_preferences_t* prefsPtr)\n{\n  LZ4_byte buf[LZ4F_HEADER_SIZE_MAX];\n  size_t ret;\n\n  if (fp == NULL || lz4fWrite == NULL)\n    return -LZ4F_ERROR_GENERIC;\n\n  *lz4fWrite = (LZ4_writeFile_t*)malloc(sizeof(LZ4_writeFile_t));\n  if (*lz4fWrite == NULL) {\n    return -LZ4F_ERROR_allocation_failed;\n  }\n  if (prefsPtr != NULL) {\n    switch (prefsPtr->frameInfo.blockSizeID) {\n      case LZ4F_default :\n      case LZ4F_max64KB :\n        (*lz4fWrite)->maxWriteSize = 64 * 1024;\n        break;\n      case LZ4F_max256KB:\n        (*lz4fWrite)->maxWriteSize = 256 * 1024;\n        break;\n      case LZ4F_max1MB:\n        (*lz4fWrite)->maxWriteSize = 1 * 1024 * 1024;\n        break;\n      case LZ4F_max4MB:\n        (*lz4fWrite)->maxWriteSize = 4 * 1024 * 1024;\n        break;\n      default:\n        free(lz4fWrite);\n        return -LZ4F_ERROR_maxBlockSize_invalid;\n      }\n    } else {\n      (*lz4fWrite)->maxWriteSize = 64 * 1024;\n    }\n\n  (*lz4fWrite)->dstBufMaxSize = LZ4F_compressBound((*lz4fWrite)->maxWriteSize, prefsPtr);\n  (*lz4fWrite)->dstBuf = (LZ4_byte*)malloc((*lz4fWrite)->dstBufMaxSize);\n  if ((*lz4fWrite)->dstBuf == NULL) {\n    free(*lz4fWrite);\n    return -LZ4F_ERROR_allocation_failed;\n  }\n\n  ret = LZ4F_createCompressionContext(&(*lz4fWrite)->cctxPtr, LZ4F_getVersion());\n  if (LZ4F_isError(ret)) {\n      free((*lz4fWrite)->dstBuf);\n      free(*lz4fWrite);\n      return ret;\n  }\n\n  ret = LZ4F_compressBegin((*lz4fWrite)->cctxPtr, buf, LZ4F_HEADER_SIZE_MAX, prefsPtr);\n  if (LZ4F_isError(ret)) {\n      LZ4F_freeCompressionContext((*lz4fWrite)->cctxPtr);\n      free((*lz4fWrite)->dstBuf);\n      free(*lz4fWrite);\n      return ret;\n  }\n\n  if (ret != fwrite(buf, 1, ret, fp)) {\n    LZ4F_freeCompressionContext((*lz4fWrite)->cctxPtr);\n    free((*lz4fWrite)->dstBuf);\n    free(*lz4fWrite);\n    return -LZ4F_ERROR_GENERIC;\n  }\n\n  (*lz4fWrite)->fp = fp;\n  (*lz4fWrite)->errCode = LZ4F_OK_NoError;\n  return LZ4F_OK_NoError;\n}\n\nsize_t LZ4F_write(LZ4_writeFile_t* lz4fWrite, void* buf, size_t size)\n{\n  LZ4_byte* p = (LZ4_byte*)buf;\n  size_t remain = size;\n  size_t chunk;\n  size_t ret;\n\n  if (lz4fWrite == NULL || buf == NULL)\n    return -LZ4F_ERROR_GENERIC;\n  while (remain) {\n    if (remain > lz4fWrite->maxWriteSize)\n      chunk = lz4fWrite->maxWriteSize;\n    else\n      chunk = remain;\n\n    ret = LZ4F_compressUpdate(lz4fWrite->cctxPtr,\n                              lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize,\n                              p, chunk,\n                              NULL);\n    if (LZ4F_isError(ret)) {\n      lz4fWrite->errCode = ret;\n      return ret;\n    }\n\n    if(ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) {\n      lz4fWrite->errCode = -LZ4F_ERROR_GENERIC;\n      return -LZ4F_ERROR_GENERIC;\n    }\n\n    p += chunk;\n    remain -= chunk;\n  }\n\n  return size;\n}\n\nLZ4F_errorCode_t LZ4F_writeClose(LZ4_writeFile_t* lz4fWrite)\n{\n  LZ4F_errorCode_t ret = LZ4F_OK_NoError;\n\n  if (lz4fWrite == NULL)\n    return -LZ4F_ERROR_GENERIC;\n\n  if (lz4fWrite->errCode == LZ4F_OK_NoError) {\n    ret =  LZ4F_compressEnd(lz4fWrite->cctxPtr,\n                            lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize,\n                            NULL);\n    if (LZ4F_isError(ret)) {\n      goto out;\n    }\n\n    if (ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) {\n      ret = -LZ4F_ERROR_GENERIC;\n    }\n  }\n\nout:\n  LZ4F_freeCompressionContext(lz4fWrite->cctxPtr);\n  free(lz4fWrite->dstBuf);\n  free(lz4fWrite);\n  return ret;\n}\n"
  },
  {
    "path": "third-party/l4z/lz4file.h",
    "content": "/*\n   LZ4 file library\n   Header File\n   Copyright (C) 2022, Xiaomi Inc.\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\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\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   You can contact the author at :\n   - LZ4 source repository : https://github.com/lz4/lz4\n   - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c\n*/\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\n#ifndef LZ4FILE_H\n#define LZ4FILE_H\n\n#include <stdio.h>\n#include \"lz4frame_static.h\"\n\ntypedef struct LZ4_readFile_s LZ4_readFile_t;\ntypedef struct LZ4_writeFile_s LZ4_writeFile_t;\n\n/*! LZ4F_readOpen() :\n * Set read lz4file handle.\n * `lz4f` will set a lz4file handle.\n * `fp` must be the return value of the lz4 file opened by fopen.\n */\nLZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_readOpen(LZ4_readFile_t** lz4fRead, FILE* fp);\n\n/*! LZ4F_read() :\n * Read lz4file content to buffer.\n * `lz4f` must use LZ4_readOpen to set first.\n * `buf` read data buffer.\n * `size` read data buffer size.\n */\nLZ4FLIB_STATIC_API size_t LZ4F_read(LZ4_readFile_t* lz4fRead, void* buf, size_t size);\n\n/*! LZ4F_readClose() :\n * Close lz4file handle.\n * `lz4f` must use LZ4_readOpen to set first.\n */\nLZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_readClose(LZ4_readFile_t* lz4fRead);\n\n/*! LZ4F_writeOpen() :\n * Set write lz4file handle.\n * `lz4f` will set a lz4file handle.\n * `fp` must be the return value of the lz4 file opened by fopen.\n */\nLZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_writeOpen(LZ4_writeFile_t** lz4fWrite, FILE* fp, const LZ4F_preferences_t* prefsPtr);\n\n/*! LZ4F_write() :\n * Write buffer to lz4file.\n * `lz4f` must use LZ4F_writeOpen to set first.\n * `buf` write data buffer.\n * `size` write data buffer size.\n */\nLZ4FLIB_STATIC_API size_t LZ4F_write(LZ4_writeFile_t* lz4fWrite, void* buf, size_t size);\n\n/*! LZ4F_writeClose() :\n * Close lz4file handle.\n * `lz4f` must use LZ4F_writeOpen to set first.\n */\nLZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_writeClose(LZ4_writeFile_t* lz4fWrite);\n\n#endif /* LZ4FILE_H */\n\n#if defined (__cplusplus)\n}\n#endif\n"
  },
  {
    "path": "third-party/l4z/lz4frame.c",
    "content": "/*\n * LZ4 auto-framing library\n * Copyright (C) 2011-2016, Yann Collet.\n *\n * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\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 *\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 * You can contact the author at :\n * - LZ4 homepage : http://www.lz4.org\n * - LZ4 source repository : https://github.com/lz4/lz4\n */\n\n/* LZ4F is a stand-alone API to create LZ4-compressed Frames\n * in full conformance with specification v1.6.1 .\n * This library rely upon memory management capabilities (malloc, free)\n * provided either by <stdlib.h>,\n * or redirected towards another library of user's choice\n * (see Memory Routines below).\n */\n\n\n/*-************************************\n*  Compiler Options\n**************************************/\n#ifdef _MSC_VER    /* Visual Studio */\n#  pragma warning(disable : 4127)   /* disable: C4127: conditional expression is constant */\n#endif\n\n\n/*-************************************\n*  Tuning parameters\n**************************************/\n/*\n * LZ4F_HEAPMODE :\n * Select how default compression functions will allocate memory for their hash table,\n * in memory stack (0:default, fastest), or in memory heap (1:requires malloc()).\n */\n#ifndef LZ4F_HEAPMODE\n#  define LZ4F_HEAPMODE 0\n#endif\n\n\n/*-************************************\n*  Library declarations\n**************************************/\n#define LZ4F_STATIC_LINKING_ONLY\n#include \"lz4frame.h\"\n#define LZ4_STATIC_LINKING_ONLY\n#include \"lz4.h\"\n#define LZ4_HC_STATIC_LINKING_ONLY\n#include \"lz4hc.h\"\n#define XXH_STATIC_LINKING_ONLY\n#include \"xxhash.h\"\n\n\n/*-************************************\n*  Memory routines\n**************************************/\n/*\n * User may redirect invocations of\n * malloc(), calloc() and free()\n * towards another library or solution of their choice\n * by modifying below section.\n**/\n\n#include <string.h>   /* memset, memcpy, memmove */\n#ifndef LZ4_SRC_INCLUDED  /* avoid redefinition when sources are coalesced */\n#  define MEM_INIT(p,v,s)   memset((p),(v),(s))\n#endif\n\n#ifndef LZ4_SRC_INCLUDED   /* avoid redefinition when sources are coalesced */\n#  include <stdlib.h>   /* malloc, calloc, free */\n#  define ALLOC(s)          malloc(s)\n#  define ALLOC_AND_ZERO(s) calloc(1,(s))\n#  define FREEMEM(p)        free(p)\n#endif\n\nstatic void* LZ4F_calloc(size_t s, LZ4F_CustomMem cmem)\n{\n    /* custom calloc defined : use it */\n    if (cmem.customCalloc != NULL) {\n        return cmem.customCalloc(cmem.opaqueState, s);\n    }\n    /* nothing defined : use default <stdlib.h>'s calloc() */\n    if (cmem.customAlloc == NULL) {\n        return ALLOC_AND_ZERO(s);\n    }\n    /* only custom alloc defined : use it, and combine it with memset() */\n    {   void* const p = cmem.customAlloc(cmem.opaqueState, s);\n        if (p != NULL) MEM_INIT(p, 0, s);\n        return p;\n}   }\n\nstatic void* LZ4F_malloc(size_t s, LZ4F_CustomMem cmem)\n{\n    /* custom malloc defined : use it */\n    if (cmem.customAlloc != NULL) {\n        return cmem.customAlloc(cmem.opaqueState, s);\n    }\n    /* nothing defined : use default <stdlib.h>'s malloc() */\n    return ALLOC(s);\n}\n\nstatic void LZ4F_free(void* p, LZ4F_CustomMem cmem)\n{\n    /* custom malloc defined : use it */\n    if (cmem.customFree != NULL) {\n        cmem.customFree(cmem.opaqueState, p);\n        return;\n    }\n    /* nothing defined : use default <stdlib.h>'s free() */\n    FREEMEM(p);\n}\n\n\n/*-************************************\n*  Debug\n**************************************/\n#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=1)\n#  include <assert.h>\n#else\n#  ifndef assert\n#    define assert(condition) ((void)0)\n#  endif\n#endif\n\n#define LZ4F_STATIC_ASSERT(c)    { enum { LZ4F_static_assert = 1/(int)(!!(c)) }; }   /* use only *after* variable declarations */\n\n#if defined(LZ4_DEBUG) && (LZ4_DEBUG>=2) && !defined(DEBUGLOG)\n#  include <stdio.h>\nstatic int g_debuglog_enable = 1;\n#  define DEBUGLOG(l, ...) {                                  \\\n                if ((g_debuglog_enable) && (l<=LZ4_DEBUG)) {  \\\n                    fprintf(stderr, __FILE__ \": \");           \\\n                    fprintf(stderr, __VA_ARGS__);             \\\n                    fprintf(stderr, \" \\n\");                   \\\n            }   }\n#else\n#  define DEBUGLOG(l, ...)      {}    /* disabled */\n#endif\n\n\n/*-************************************\n*  Basic Types\n**************************************/\n#if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )\n# include <stdint.h>\n  typedef  uint8_t BYTE;\n  typedef uint16_t U16;\n  typedef uint32_t U32;\n  typedef  int32_t S32;\n  typedef uint64_t U64;\n#else\n  typedef unsigned char       BYTE;\n  typedef unsigned short      U16;\n  typedef unsigned int        U32;\n  typedef   signed int        S32;\n  typedef unsigned long long  U64;\n#endif\n\n\n/* unoptimized version; solves endianness & alignment issues */\nstatic U32 LZ4F_readLE32 (const void* src)\n{\n    const BYTE* const srcPtr = (const BYTE*)src;\n    U32 value32 = srcPtr[0];\n    value32 += ((U32)srcPtr[1])<< 8;\n    value32 += ((U32)srcPtr[2])<<16;\n    value32 += ((U32)srcPtr[3])<<24;\n    return value32;\n}\n\nstatic void LZ4F_writeLE32 (void* dst, U32 value32)\n{\n    BYTE* const dstPtr = (BYTE*)dst;\n    dstPtr[0] = (BYTE)value32;\n    dstPtr[1] = (BYTE)(value32 >> 8);\n    dstPtr[2] = (BYTE)(value32 >> 16);\n    dstPtr[3] = (BYTE)(value32 >> 24);\n}\n\nstatic U64 LZ4F_readLE64 (const void* src)\n{\n    const BYTE* const srcPtr = (const BYTE*)src;\n    U64 value64 = srcPtr[0];\n    value64 += ((U64)srcPtr[1]<<8);\n    value64 += ((U64)srcPtr[2]<<16);\n    value64 += ((U64)srcPtr[3]<<24);\n    value64 += ((U64)srcPtr[4]<<32);\n    value64 += ((U64)srcPtr[5]<<40);\n    value64 += ((U64)srcPtr[6]<<48);\n    value64 += ((U64)srcPtr[7]<<56);\n    return value64;\n}\n\nstatic void LZ4F_writeLE64 (void* dst, U64 value64)\n{\n    BYTE* const dstPtr = (BYTE*)dst;\n    dstPtr[0] = (BYTE)value64;\n    dstPtr[1] = (BYTE)(value64 >> 8);\n    dstPtr[2] = (BYTE)(value64 >> 16);\n    dstPtr[3] = (BYTE)(value64 >> 24);\n    dstPtr[4] = (BYTE)(value64 >> 32);\n    dstPtr[5] = (BYTE)(value64 >> 40);\n    dstPtr[6] = (BYTE)(value64 >> 48);\n    dstPtr[7] = (BYTE)(value64 >> 56);\n}\n\n\n/*-************************************\n*  Constants\n**************************************/\n#ifndef LZ4_SRC_INCLUDED   /* avoid double definition */\n#  define KB *(1<<10)\n#  define MB *(1<<20)\n#  define GB *(1<<30)\n#endif\n\n#define _1BIT  0x01\n#define _2BITS 0x03\n#define _3BITS 0x07\n#define _4BITS 0x0F\n#define _8BITS 0xFF\n\n#define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U\n#define LZ4F_BLOCKSIZEID_DEFAULT LZ4F_max64KB\n\nstatic const size_t minFHSize = LZ4F_HEADER_SIZE_MIN;   /*  7 */\nstatic const size_t maxFHSize = LZ4F_HEADER_SIZE_MAX;   /* 19 */\nstatic const size_t BHSize = LZ4F_BLOCK_HEADER_SIZE;  /* block header : size, and compress flag */\nstatic const size_t BFSize = LZ4F_BLOCK_CHECKSUM_SIZE;  /* block footer : checksum (optional) */\n\n\n/*-************************************\n*  Structures and local types\n**************************************/\n\ntypedef enum { LZ4B_COMPRESSED, LZ4B_UNCOMPRESSED} LZ4F_blockCompression_t;\n\ntypedef struct LZ4F_cctx_s\n{\n    LZ4F_CustomMem cmem;\n    LZ4F_preferences_t prefs;\n    U32    version;\n    U32    cStage;\n    const LZ4F_CDict* cdict;\n    size_t maxBlockSize;\n    size_t maxBufferSize;\n    BYTE*  tmpBuff;    /* internal buffer, for streaming */\n    BYTE*  tmpIn;      /* starting position of data compress within internal buffer (>= tmpBuff) */\n    size_t tmpInSize;  /* amount of data to compress after tmpIn */\n    U64    totalInSize;\n    XXH32_state_t xxh;\n    void*  lz4CtxPtr;\n    U16    lz4CtxAlloc; /* sized for: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */\n    U16    lz4CtxState; /* in use as: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */\n    LZ4F_blockCompression_t  blockCompression;\n} LZ4F_cctx_t;\n\n\n/*-************************************\n*  Error management\n**************************************/\n#define LZ4F_GENERATE_STRING(STRING) #STRING,\nstatic const char* LZ4F_errorStrings[] = { LZ4F_LIST_ERRORS(LZ4F_GENERATE_STRING) };\n\n\nunsigned LZ4F_isError(LZ4F_errorCode_t code)\n{\n    return (code > (LZ4F_errorCode_t)(-LZ4F_ERROR_maxCode));\n}\n\nconst char* LZ4F_getErrorName(LZ4F_errorCode_t code)\n{\n    static const char* codeError = \"Unspecified error code\";\n    if (LZ4F_isError(code)) return LZ4F_errorStrings[-(int)(code)];\n    return codeError;\n}\n\nLZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult)\n{\n    if (!LZ4F_isError(functionResult)) return LZ4F_OK_NoError;\n    return (LZ4F_errorCodes)(-(ptrdiff_t)functionResult);\n}\n\nstatic LZ4F_errorCode_t LZ4F_returnErrorCode(LZ4F_errorCodes code)\n{\n    /* A compilation error here means sizeof(ptrdiff_t) is not large enough */\n    LZ4F_STATIC_ASSERT(sizeof(ptrdiff_t) >= sizeof(size_t));\n    return (LZ4F_errorCode_t)-(ptrdiff_t)code;\n}\n\n#define RETURN_ERROR(e) return LZ4F_returnErrorCode(LZ4F_ERROR_ ## e)\n\n#define RETURN_ERROR_IF(c,e) if (c) RETURN_ERROR(e)\n\n#define FORWARD_IF_ERROR(r)  if (LZ4F_isError(r)) return (r)\n\nunsigned LZ4F_getVersion(void) { return LZ4F_VERSION; }\n\nint LZ4F_compressionLevel_max(void) { return LZ4HC_CLEVEL_MAX; }\n\nsize_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID)\n{\n    static const size_t blockSizes[4] = { 64 KB, 256 KB, 1 MB, 4 MB };\n\n    if (blockSizeID == 0) blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;\n    if (blockSizeID < LZ4F_max64KB || blockSizeID > LZ4F_max4MB)\n        RETURN_ERROR(maxBlockSize_invalid);\n    {   int const blockSizeIdx = (int)blockSizeID - (int)LZ4F_max64KB;\n        return blockSizes[blockSizeIdx];\n}   }\n\n/*-************************************\n*  Private functions\n**************************************/\n#define MIN(a,b)   ( (a) < (b) ? (a) : (b) )\n\nstatic BYTE LZ4F_headerChecksum (const void* header, size_t length)\n{\n    U32 const xxh = XXH32(header, length, 0);\n    return (BYTE)(xxh >> 8);\n}\n\n\n/*-************************************\n*  Simple-pass compression functions\n**************************************/\nstatic LZ4F_blockSizeID_t LZ4F_optimalBSID(const LZ4F_blockSizeID_t requestedBSID,\n                                           const size_t srcSize)\n{\n    LZ4F_blockSizeID_t proposedBSID = LZ4F_max64KB;\n    size_t maxBlockSize = 64 KB;\n    while (requestedBSID > proposedBSID) {\n        if (srcSize <= maxBlockSize)\n            return proposedBSID;\n        proposedBSID = (LZ4F_blockSizeID_t)((int)proposedBSID + 1);\n        maxBlockSize <<= 2;\n    }\n    return requestedBSID;\n}\n\n/*! LZ4F_compressBound_internal() :\n *  Provides dstCapacity given a srcSize to guarantee operation success in worst case situations.\n *  prefsPtr is optional : if NULL is provided, preferences will be set to cover worst case scenario.\n * @return is always the same for a srcSize and prefsPtr, so it can be relied upon to size reusable buffers.\n *  When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() operations.\n */\nstatic size_t LZ4F_compressBound_internal(size_t srcSize,\n                                    const LZ4F_preferences_t* preferencesPtr,\n                                          size_t alreadyBuffered)\n{\n    LZ4F_preferences_t prefsNull = LZ4F_INIT_PREFERENCES;\n    prefsNull.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled;   /* worst case */\n    prefsNull.frameInfo.blockChecksumFlag = LZ4F_blockChecksumEnabled;   /* worst case */\n    {   const LZ4F_preferences_t* const prefsPtr = (preferencesPtr==NULL) ? &prefsNull : preferencesPtr;\n        U32 const flush = prefsPtr->autoFlush | (srcSize==0);\n        LZ4F_blockSizeID_t const blockID = prefsPtr->frameInfo.blockSizeID;\n        size_t const blockSize = LZ4F_getBlockSize(blockID);\n        size_t const maxBuffered = blockSize - 1;\n        size_t const bufferedSize = MIN(alreadyBuffered, maxBuffered);\n        size_t const maxSrcSize = srcSize + bufferedSize;\n        unsigned const nbFullBlocks = (unsigned)(maxSrcSize / blockSize);\n        size_t const partialBlockSize = maxSrcSize & (blockSize-1);\n        size_t const lastBlockSize = flush ? partialBlockSize : 0;\n        unsigned const nbBlocks = nbFullBlocks + (lastBlockSize>0);\n\n        size_t const blockCRCSize = BFSize * prefsPtr->frameInfo.blockChecksumFlag;\n        size_t const frameEnd = BHSize + (prefsPtr->frameInfo.contentChecksumFlag*BFSize);\n\n        return ((BHSize + blockCRCSize) * nbBlocks) +\n               (blockSize * nbFullBlocks) + lastBlockSize + frameEnd;\n    }\n}\n\nsize_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)\n{\n    LZ4F_preferences_t prefs;\n    size_t const headerSize = maxFHSize;      /* max header size, including optional fields */\n\n    if (preferencesPtr!=NULL) prefs = *preferencesPtr;\n    else MEM_INIT(&prefs, 0, sizeof(prefs));\n    prefs.autoFlush = 1;\n\n    return headerSize + LZ4F_compressBound_internal(srcSize, &prefs, 0);;\n}\n\n\n/*! LZ4F_compressFrame_usingCDict() :\n *  Compress srcBuffer using a dictionary, in a single step.\n *  cdict can be NULL, in which case, no dictionary is used.\n *  dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).\n *  The LZ4F_preferences_t structure is optional : you may provide NULL as argument,\n *  however, it's the only way to provide a dictID, so it's not recommended.\n * @return : number of bytes written into dstBuffer,\n *           or an error code if it fails (can be tested using LZ4F_isError())\n */\nsize_t LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx,\n                                     void* dstBuffer, size_t dstCapacity,\n                               const void* srcBuffer, size_t srcSize,\n                               const LZ4F_CDict* cdict,\n                               const LZ4F_preferences_t* preferencesPtr)\n{\n    LZ4F_preferences_t prefs;\n    LZ4F_compressOptions_t options;\n    BYTE* const dstStart = (BYTE*) dstBuffer;\n    BYTE* dstPtr = dstStart;\n    BYTE* const dstEnd = dstStart + dstCapacity;\n\n    if (preferencesPtr!=NULL)\n        prefs = *preferencesPtr;\n    else\n        MEM_INIT(&prefs, 0, sizeof(prefs));\n    if (prefs.frameInfo.contentSize != 0)\n        prefs.frameInfo.contentSize = (U64)srcSize;   /* auto-correct content size if selected (!=0) */\n\n    prefs.frameInfo.blockSizeID = LZ4F_optimalBSID(prefs.frameInfo.blockSizeID, srcSize);\n    prefs.autoFlush = 1;\n    if (srcSize <= LZ4F_getBlockSize(prefs.frameInfo.blockSizeID))\n        prefs.frameInfo.blockMode = LZ4F_blockIndependent;   /* only one block => no need for inter-block link */\n\n    MEM_INIT(&options, 0, sizeof(options));\n    options.stableSrc = 1;\n\n    RETURN_ERROR_IF(dstCapacity < LZ4F_compressFrameBound(srcSize, &prefs), dstMaxSize_tooSmall);\n\n    { size_t const headerSize = LZ4F_compressBegin_usingCDict(cctx, dstBuffer, dstCapacity, cdict, &prefs);  /* write header */\n      FORWARD_IF_ERROR(headerSize);\n      dstPtr += headerSize;   /* header size */ }\n\n    assert(dstEnd >= dstPtr);\n    { size_t const cSize = LZ4F_compressUpdate(cctx, dstPtr, (size_t)(dstEnd-dstPtr), srcBuffer, srcSize, &options);\n      FORWARD_IF_ERROR(cSize);\n      dstPtr += cSize; }\n\n    assert(dstEnd >= dstPtr);\n    { size_t const tailSize = LZ4F_compressEnd(cctx, dstPtr, (size_t)(dstEnd-dstPtr), &options);   /* flush last block, and generate suffix */\n      FORWARD_IF_ERROR(tailSize);\n      dstPtr += tailSize; }\n\n    assert(dstEnd >= dstStart);\n    return (size_t)(dstPtr - dstStart);\n}\n\n\n/*! LZ4F_compressFrame() :\n *  Compress an entire srcBuffer into a valid LZ4 frame, in a single step.\n *  dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).\n *  The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default.\n * @return : number of bytes written into dstBuffer.\n *           or an error code if it fails (can be tested using LZ4F_isError())\n */\nsize_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity,\n                    const void* srcBuffer, size_t srcSize,\n                    const LZ4F_preferences_t* preferencesPtr)\n{\n    size_t result;\n#if (LZ4F_HEAPMODE)\n    LZ4F_cctx_t* cctxPtr;\n    result = LZ4F_createCompressionContext(&cctxPtr, LZ4F_VERSION);\n    FORWARD_IF_ERROR(result);\n#else\n    LZ4F_cctx_t cctx;\n    LZ4_stream_t lz4ctx;\n    LZ4F_cctx_t* const cctxPtr = &cctx;\n\n    MEM_INIT(&cctx, 0, sizeof(cctx));\n    cctx.version = LZ4F_VERSION;\n    cctx.maxBufferSize = 5 MB;   /* mess with real buffer size to prevent dynamic allocation; works only because autoflush==1 & stableSrc==1 */\n    if ( preferencesPtr == NULL\n      || preferencesPtr->compressionLevel < LZ4HC_CLEVEL_MIN ) {\n        LZ4_initStream(&lz4ctx, sizeof(lz4ctx));\n        cctxPtr->lz4CtxPtr = &lz4ctx;\n        cctxPtr->lz4CtxAlloc = 1;\n        cctxPtr->lz4CtxState = 1;\n    }\n#endif\n    DEBUGLOG(4, \"LZ4F_compressFrame\");\n\n    result = LZ4F_compressFrame_usingCDict(cctxPtr, dstBuffer, dstCapacity,\n                                           srcBuffer, srcSize,\n                                           NULL, preferencesPtr);\n\n#if (LZ4F_HEAPMODE)\n    LZ4F_freeCompressionContext(cctxPtr);\n#else\n    if ( preferencesPtr != NULL\n      && preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN ) {\n        LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);\n    }\n#endif\n    return result;\n}\n\n\n/*-***************************************************\n*   Dictionary compression\n*****************************************************/\n\nstruct LZ4F_CDict_s {\n    LZ4F_CustomMem cmem;\n    void* dictContent;\n    LZ4_stream_t* fastCtx;\n    LZ4_streamHC_t* HCCtx;\n}; /* typedef'd to LZ4F_CDict within lz4frame_static.h */\n\nLZ4F_CDict*\nLZ4F_createCDict_advanced(LZ4F_CustomMem cmem, const void* dictBuffer, size_t dictSize)\n{\n    const char* dictStart = (const char*)dictBuffer;\n    LZ4F_CDict* const cdict = (LZ4F_CDict*)LZ4F_malloc(sizeof(*cdict), cmem);\n    DEBUGLOG(4, \"LZ4F_createCDict_advanced\");\n    if (!cdict) return NULL;\n    cdict->cmem = cmem;\n    if (dictSize > 64 KB) {\n        dictStart += dictSize - 64 KB;\n        dictSize = 64 KB;\n    }\n    cdict->dictContent = LZ4F_malloc(dictSize, cmem);\n    cdict->fastCtx = (LZ4_stream_t*)LZ4F_malloc(sizeof(LZ4_stream_t), cmem);\n    if (cdict->fastCtx)\n        LZ4_initStream(cdict->fastCtx, sizeof(LZ4_stream_t));\n    cdict->HCCtx = (LZ4_streamHC_t*)LZ4F_malloc(sizeof(LZ4_streamHC_t), cmem);\n    if (cdict->HCCtx)\n        LZ4_initStream(cdict->HCCtx, sizeof(LZ4_streamHC_t));\n    if (!cdict->dictContent || !cdict->fastCtx || !cdict->HCCtx) {\n        LZ4F_freeCDict(cdict);\n        return NULL;\n    }\n    memcpy(cdict->dictContent, dictStart, dictSize);\n    LZ4_loadDict (cdict->fastCtx, (const char*)cdict->dictContent, (int)dictSize);\n    LZ4_setCompressionLevel(cdict->HCCtx, LZ4HC_CLEVEL_DEFAULT);\n    LZ4_loadDictHC(cdict->HCCtx, (const char*)cdict->dictContent, (int)dictSize);\n    return cdict;\n}\n\n/*! LZ4F_createCDict() :\n *  When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once.\n *  LZ4F_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay.\n *  LZ4F_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.\n * @dictBuffer can be released after LZ4F_CDict creation, since its content is copied within CDict\n * @return : digested dictionary for compression, or NULL if failed */\nLZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize)\n{\n    DEBUGLOG(4, \"LZ4F_createCDict\");\n    return LZ4F_createCDict_advanced(LZ4F_defaultCMem, dictBuffer, dictSize);\n}\n\nvoid LZ4F_freeCDict(LZ4F_CDict* cdict)\n{\n    if (cdict==NULL) return;  /* support free on NULL */\n    LZ4F_free(cdict->dictContent, cdict->cmem);\n    LZ4F_free(cdict->fastCtx, cdict->cmem);\n    LZ4F_free(cdict->HCCtx, cdict->cmem);\n    LZ4F_free(cdict, cdict->cmem);\n}\n\n\n/*-*********************************\n*  Advanced compression functions\n***********************************/\n\nLZ4F_cctx*\nLZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version)\n{\n    LZ4F_cctx* const cctxPtr =\n        (LZ4F_cctx*)LZ4F_calloc(sizeof(LZ4F_cctx), customMem);\n    if (cctxPtr==NULL) return NULL;\n\n    cctxPtr->cmem = customMem;\n    cctxPtr->version = version;\n    cctxPtr->cStage = 0;   /* Uninitialized. Next stage : init cctx */\n\n    return cctxPtr;\n}\n\n/*! LZ4F_createCompressionContext() :\n *  The first thing to do is to create a compressionContext object, which will be used in all compression operations.\n *  This is achieved using LZ4F_createCompressionContext(), which takes as argument a version and an LZ4F_preferences_t structure.\n *  The version provided MUST be LZ4F_VERSION. It is intended to track potential incompatible differences between different binaries.\n *  The function will provide a pointer to an allocated LZ4F_compressionContext_t object.\n *  If the result LZ4F_errorCode_t is not OK_NoError, there was an error during context creation.\n *  Object can release its memory using LZ4F_freeCompressionContext();\n**/\nLZ4F_errorCode_t\nLZ4F_createCompressionContext(LZ4F_cctx** LZ4F_compressionContextPtr, unsigned version)\n{\n    assert(LZ4F_compressionContextPtr != NULL); /* considered a violation of narrow contract */\n    /* in case it nonetheless happen in production */\n    RETURN_ERROR_IF(LZ4F_compressionContextPtr == NULL, parameter_null);\n\n    *LZ4F_compressionContextPtr = LZ4F_createCompressionContext_advanced(LZ4F_defaultCMem, version);\n    RETURN_ERROR_IF(*LZ4F_compressionContextPtr==NULL, allocation_failed);\n    return LZ4F_OK_NoError;\n}\n\n\nLZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctxPtr)\n{\n    if (cctxPtr != NULL) {  /* support free on NULL */\n       LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);  /* note: LZ4_streamHC_t and LZ4_stream_t are simple POD types */\n       LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem);\n       LZ4F_free(cctxPtr, cctxPtr->cmem);\n    }\n    return LZ4F_OK_NoError;\n}\n\n\n/**\n * This function prepares the internal LZ4(HC) stream for a new compression,\n * resetting the context and attaching the dictionary, if there is one.\n *\n * It needs to be called at the beginning of each independent compression\n * stream (i.e., at the beginning of a frame in blockLinked mode, or at the\n * beginning of each block in blockIndependent mode).\n */\nstatic void LZ4F_initStream(void* ctx,\n                            const LZ4F_CDict* cdict,\n                            int level,\n                            LZ4F_blockMode_t blockMode) {\n    if (level < LZ4HC_CLEVEL_MIN) {\n        if (cdict != NULL || blockMode == LZ4F_blockLinked) {\n            /* In these cases, we will call LZ4_compress_fast_continue(),\n             * which needs an already reset context. Otherwise, we'll call a\n             * one-shot API. The non-continued APIs internally perform their own\n             * resets at the beginning of their calls, where they know what\n             * tableType they need the context to be in. So in that case this\n             * would be misguided / wasted work. */\n            LZ4_resetStream_fast((LZ4_stream_t*)ctx);\n        }\n        LZ4_attach_dictionary((LZ4_stream_t *)ctx, cdict ? cdict->fastCtx : NULL);\n    } else {\n        LZ4_resetStreamHC_fast((LZ4_streamHC_t*)ctx, level);\n        LZ4_attach_HC_dictionary((LZ4_streamHC_t *)ctx, cdict ? cdict->HCCtx : NULL);\n    }\n}\n\nstatic int ctxTypeID_to_size(int ctxTypeID) {\n    switch(ctxTypeID) {\n    case 1:\n        return LZ4_sizeofState();\n    case 2:\n        return LZ4_sizeofStateHC();\n    default:\n        return 0;\n    }\n}\n\n/*! LZ4F_compressBegin_usingCDict() :\n *  init streaming compression AND writes frame header into @dstBuffer.\n * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.\n * @return : number of bytes written into @dstBuffer for the header\n *           or an error code (can be tested using LZ4F_isError())\n */\nsize_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctxPtr,\n                          void* dstBuffer, size_t dstCapacity,\n                          const LZ4F_CDict* cdict,\n                          const LZ4F_preferences_t* preferencesPtr)\n{\n    LZ4F_preferences_t const prefNull = LZ4F_INIT_PREFERENCES;\n    BYTE* const dstStart = (BYTE*)dstBuffer;\n    BYTE* dstPtr = dstStart;\n\n    RETURN_ERROR_IF(dstCapacity < maxFHSize, dstMaxSize_tooSmall);\n    if (preferencesPtr == NULL) preferencesPtr = &prefNull;\n    cctxPtr->prefs = *preferencesPtr;\n\n    /* cctx Management */\n    {   U16 const ctxTypeID = (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) ? 1 : 2;\n        int requiredSize = ctxTypeID_to_size(ctxTypeID);\n        int allocatedSize = ctxTypeID_to_size(cctxPtr->lz4CtxAlloc);\n        if (allocatedSize < requiredSize) {\n            /* not enough space allocated */\n            LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem);\n            if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) {\n                /* must take ownership of memory allocation,\n                 * in order to respect custom allocator contract */\n                cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_stream_t), cctxPtr->cmem);\n                if (cctxPtr->lz4CtxPtr)\n                    LZ4_initStream(cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t));\n            } else {\n                cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_streamHC_t), cctxPtr->cmem);\n                if (cctxPtr->lz4CtxPtr)\n                    LZ4_initStreamHC(cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t));\n            }\n            RETURN_ERROR_IF(cctxPtr->lz4CtxPtr == NULL, allocation_failed);\n            cctxPtr->lz4CtxAlloc = ctxTypeID;\n            cctxPtr->lz4CtxState = ctxTypeID;\n        } else if (cctxPtr->lz4CtxState != ctxTypeID) {\n            /* otherwise, a sufficient buffer is already allocated,\n             * but we need to reset it to the correct context type */\n            if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) {\n                LZ4_initStream((LZ4_stream_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t));\n            } else {\n                LZ4_initStreamHC((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t));\n                LZ4_setCompressionLevel((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel);\n            }\n            cctxPtr->lz4CtxState = ctxTypeID;\n    }   }\n\n    /* Buffer Management */\n    if (cctxPtr->prefs.frameInfo.blockSizeID == 0)\n        cctxPtr->prefs.frameInfo.blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT;\n    cctxPtr->maxBlockSize = LZ4F_getBlockSize(cctxPtr->prefs.frameInfo.blockSizeID);\n\n    {   size_t const requiredBuffSize = preferencesPtr->autoFlush ?\n                ((cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 64 KB : 0) :  /* only needs past data up to window size */\n                cctxPtr->maxBlockSize + ((cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) ? 128 KB : 0);\n\n        if (cctxPtr->maxBufferSize < requiredBuffSize) {\n            cctxPtr->maxBufferSize = 0;\n            LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem);\n            cctxPtr->tmpBuff = (BYTE*)LZ4F_calloc(requiredBuffSize, cctxPtr->cmem);\n            RETURN_ERROR_IF(cctxPtr->tmpBuff == NULL, allocation_failed);\n            cctxPtr->maxBufferSize = requiredBuffSize;\n    }   }\n    cctxPtr->tmpIn = cctxPtr->tmpBuff;\n    cctxPtr->tmpInSize = 0;\n    (void)XXH32_reset(&(cctxPtr->xxh), 0);\n\n    /* context init */\n    cctxPtr->cdict = cdict;\n    if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked) {\n        /* frame init only for blockLinked : blockIndependent will be init at each block */\n        LZ4F_initStream(cctxPtr->lz4CtxPtr, cdict, cctxPtr->prefs.compressionLevel, LZ4F_blockLinked);\n    }\n    if (preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN) {\n        LZ4_favorDecompressionSpeed((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, (int)preferencesPtr->favorDecSpeed);\n    }\n\n    /* Magic Number */\n    LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER);\n    dstPtr += 4;\n    {   BYTE* const headerStart = dstPtr;\n\n        /* FLG Byte */\n        *dstPtr++ = (BYTE)(((1 & _2BITS) << 6)    /* Version('01') */\n            + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5)\n            + ((cctxPtr->prefs.frameInfo.blockChecksumFlag & _1BIT ) << 4)\n            + ((unsigned)(cctxPtr->prefs.frameInfo.contentSize > 0) << 3)\n            + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2)\n            +  (cctxPtr->prefs.frameInfo.dictID > 0) );\n        /* BD Byte */\n        *dstPtr++ = (BYTE)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4);\n        /* Optional Frame content size field */\n        if (cctxPtr->prefs.frameInfo.contentSize) {\n            LZ4F_writeLE64(dstPtr, cctxPtr->prefs.frameInfo.contentSize);\n            dstPtr += 8;\n            cctxPtr->totalInSize = 0;\n        }\n        /* Optional dictionary ID field */\n        if (cctxPtr->prefs.frameInfo.dictID) {\n            LZ4F_writeLE32(dstPtr, cctxPtr->prefs.frameInfo.dictID);\n            dstPtr += 4;\n        }\n        /* Header CRC Byte */\n        *dstPtr = LZ4F_headerChecksum(headerStart, (size_t)(dstPtr - headerStart));\n        dstPtr++;\n    }\n\n    cctxPtr->cStage = 1;   /* header written, now request input data block */\n    return (size_t)(dstPtr - dstStart);\n}\n\n\n/*! LZ4F_compressBegin() :\n *  init streaming compression AND writes frame header into @dstBuffer.\n * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.\n * @preferencesPtr can be NULL, in which case default parameters are selected.\n * @return : number of bytes written into dstBuffer for the header\n *        or an error code (can be tested using LZ4F_isError())\n */\nsize_t LZ4F_compressBegin(LZ4F_cctx* cctxPtr,\n                          void* dstBuffer, size_t dstCapacity,\n                          const LZ4F_preferences_t* preferencesPtr)\n{\n    return LZ4F_compressBegin_usingCDict(cctxPtr, dstBuffer, dstCapacity,\n                                         NULL, preferencesPtr);\n}\n\n\n/*  LZ4F_compressBound() :\n * @return minimum capacity of dstBuffer for a given srcSize to handle worst case scenario.\n *  LZ4F_preferences_t structure is optional : if NULL, preferences will be set to cover worst case scenario.\n *  This function cannot fail.\n */\nsize_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr)\n{\n    if (preferencesPtr && preferencesPtr->autoFlush) {\n        return LZ4F_compressBound_internal(srcSize, preferencesPtr, 0);\n    }\n    return LZ4F_compressBound_internal(srcSize, preferencesPtr, (size_t)-1);\n}\n\n\ntypedef int (*compressFunc_t)(void* ctx, const char* src, char* dst, int srcSize, int dstSize, int level, const LZ4F_CDict* cdict);\n\n\n/*! LZ4F_makeBlock():\n *  compress a single block, add header and optional checksum.\n *  assumption : dst buffer capacity is >= BHSize + srcSize + crcSize\n */\nstatic size_t LZ4F_makeBlock(void* dst,\n                       const void* src, size_t srcSize,\n                             compressFunc_t compress, void* lz4ctx, int level,\n                       const LZ4F_CDict* cdict,\n                             LZ4F_blockChecksum_t crcFlag)\n{\n    BYTE* const cSizePtr = (BYTE*)dst;\n    U32 cSize;\n    assert(compress != NULL);\n    cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+BHSize),\n                          (int)(srcSize), (int)(srcSize-1),\n                          level, cdict);\n\n    if (cSize == 0 || cSize >= srcSize) {\n        cSize = (U32)srcSize;\n        LZ4F_writeLE32(cSizePtr, cSize | LZ4F_BLOCKUNCOMPRESSED_FLAG);\n        memcpy(cSizePtr+BHSize, src, srcSize);\n    } else {\n        LZ4F_writeLE32(cSizePtr, cSize);\n    }\n    if (crcFlag) {\n        U32 const crc32 = XXH32(cSizePtr+BHSize, cSize, 0);  /* checksum of compressed data */\n        LZ4F_writeLE32(cSizePtr+BHSize+cSize, crc32);\n    }\n    return BHSize + cSize + ((U32)crcFlag)*BFSize;\n}\n\n\nstatic int LZ4F_compressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)\n{\n    int const acceleration = (level < 0) ? -level + 1 : 1;\n    DEBUGLOG(5, \"LZ4F_compressBlock (srcSize=%i)\", srcSize);\n    LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent);\n    if (cdict) {\n        return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration);\n    } else {\n        return LZ4_compress_fast_extState_fastReset(ctx, src, dst, srcSize, dstCapacity, acceleration);\n    }\n}\n\nstatic int LZ4F_compressBlock_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)\n{\n    int const acceleration = (level < 0) ? -level + 1 : 1;\n    (void)cdict; /* init once at beginning of frame */\n    DEBUGLOG(5, \"LZ4F_compressBlock_continue (srcSize=%i)\", srcSize);\n    return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration);\n}\n\nstatic int LZ4F_compressBlockHC(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)\n{\n    LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent);\n    if (cdict) {\n        return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity);\n    }\n    return LZ4_compress_HC_extStateHC_fastReset(ctx, src, dst, srcSize, dstCapacity, level);\n}\n\nstatic int LZ4F_compressBlockHC_continue(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)\n{\n    (void)level; (void)cdict; /* init once at beginning of frame */\n    return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity);\n}\n\nstatic int LZ4F_doNotCompressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict)\n{\n    (void)ctx; (void)src; (void)dst; (void)srcSize; (void)dstCapacity; (void)level; (void)cdict;\n    return 0;\n}\n\nstatic compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int level, LZ4F_blockCompression_t  compressMode)\n{\n    if (compressMode == LZ4B_UNCOMPRESSED) return LZ4F_doNotCompressBlock;\n    if (level < LZ4HC_CLEVEL_MIN) {\n        if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlock;\n        return LZ4F_compressBlock_continue;\n    }\n    if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlockHC;\n    return LZ4F_compressBlockHC_continue;\n}\n\n/* Save history (up to 64KB) into @tmpBuff */\nstatic int LZ4F_localSaveDict(LZ4F_cctx_t* cctxPtr)\n{\n    if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN)\n        return LZ4_saveDict ((LZ4_stream_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);\n    return LZ4_saveDictHC ((LZ4_streamHC_t*)(cctxPtr->lz4CtxPtr), (char*)(cctxPtr->tmpBuff), 64 KB);\n}\n\ntypedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus;\n\nstatic const LZ4F_compressOptions_t k_cOptionsNull = { 0, { 0, 0, 0 } };\n\n\n /*! LZ4F_compressUpdateImpl() :\n *  LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.\n *  When successful, the function always entirely consumes @srcBuffer.\n *  src data is either buffered or compressed into @dstBuffer.\n *  If the block compression does not match the compression of the previous block, the old data is flushed\n *  and operations continue with the new compression mode.\n * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr) when block compression is turned on.\n * @compressOptionsPtr is optional : provide NULL to mean \"default\".\n * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.\n *           or an error code if it fails (which can be tested using LZ4F_isError())\n *  After an error, the state is left in a UB state, and must be re-initialized.\n */\nstatic size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr,\n                     void* dstBuffer, size_t dstCapacity,\n                     const void* srcBuffer, size_t srcSize,\n                     const LZ4F_compressOptions_t* compressOptionsPtr,\n                     LZ4F_blockCompression_t blockCompression)\n  {\n    size_t const blockSize = cctxPtr->maxBlockSize;\n    const BYTE* srcPtr = (const BYTE*)srcBuffer;\n    const BYTE* const srcEnd = srcPtr + srcSize;\n    BYTE* const dstStart = (BYTE*)dstBuffer;\n    BYTE* dstPtr = dstStart;\n    LZ4F_lastBlockStatus lastBlockCompressed = notDone;\n    compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, blockCompression);\n    size_t bytesWritten;\n    DEBUGLOG(4, \"LZ4F_compressUpdate (srcSize=%zu)\", srcSize);\n\n    RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized);   /* state must be initialized and waiting for next block */\n    if (dstCapacity < LZ4F_compressBound_internal(srcSize, &(cctxPtr->prefs), cctxPtr->tmpInSize))\n        RETURN_ERROR(dstMaxSize_tooSmall);\n\n    if (blockCompression == LZ4B_UNCOMPRESSED && dstCapacity < srcSize)\n        RETURN_ERROR(dstMaxSize_tooSmall);\n\n    /* flush currently written block, to continue with new block compression */\n    if (cctxPtr->blockCompression != blockCompression) {\n        bytesWritten = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr);\n        dstPtr += bytesWritten;\n        cctxPtr->blockCompression = blockCompression;\n    }\n\n    if (compressOptionsPtr == NULL) compressOptionsPtr = &k_cOptionsNull;\n\n    /* complete tmp buffer */\n    if (cctxPtr->tmpInSize > 0) {   /* some data already within tmp buffer */\n        size_t const sizeToCopy = blockSize - cctxPtr->tmpInSize;\n        assert(blockSize > cctxPtr->tmpInSize);\n        if (sizeToCopy > srcSize) {\n            /* add src to tmpIn buffer */\n            memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize);\n            srcPtr = srcEnd;\n            cctxPtr->tmpInSize += srcSize;\n            /* still needs some CRC */\n        } else {\n            /* complete tmpIn block and then compress it */\n            lastBlockCompressed = fromTmpBuffer;\n            memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, sizeToCopy);\n            srcPtr += sizeToCopy;\n\n            dstPtr += LZ4F_makeBlock(dstPtr,\n                                     cctxPtr->tmpIn, blockSize,\n                                     compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,\n                                     cctxPtr->cdict,\n                                     cctxPtr->prefs.frameInfo.blockChecksumFlag);\n            if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += blockSize;\n            cctxPtr->tmpInSize = 0;\n    }   }\n\n    while ((size_t)(srcEnd - srcPtr) >= blockSize) {\n        /* compress full blocks */\n        lastBlockCompressed = fromSrcBuffer;\n        dstPtr += LZ4F_makeBlock(dstPtr,\n                                 srcPtr, blockSize,\n                                 compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,\n                                 cctxPtr->cdict,\n                                 cctxPtr->prefs.frameInfo.blockChecksumFlag);\n        srcPtr += blockSize;\n    }\n\n    if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd)) {\n        /* autoFlush : remaining input (< blockSize) is compressed */\n        lastBlockCompressed = fromSrcBuffer;\n        dstPtr += LZ4F_makeBlock(dstPtr,\n                                 srcPtr, (size_t)(srcEnd - srcPtr),\n                                 compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,\n                                 cctxPtr->cdict,\n                                 cctxPtr->prefs.frameInfo.blockChecksumFlag);\n        srcPtr = srcEnd;\n    }\n\n    /* preserve dictionary within @tmpBuff whenever necessary */\n    if ((cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) && (lastBlockCompressed==fromSrcBuffer)) {\n        /* linked blocks are only supported in compressed mode, see LZ4F_uncompressedUpdate */\n        assert(blockCompression == LZ4B_COMPRESSED);\n        if (compressOptionsPtr->stableSrc) {\n            cctxPtr->tmpIn = cctxPtr->tmpBuff;  /* src is stable : dictionary remains in src across invocations */\n        } else {\n            int const realDictSize = LZ4F_localSaveDict(cctxPtr);\n            assert(0 <= realDictSize && realDictSize <= 64 KB);\n            cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;\n        }\n    }\n\n    /* keep tmpIn within limits */\n    if (!(cctxPtr->prefs.autoFlush)  /* no autoflush : there may be some data left within internal buffer */\n      && (cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize) )  /* not enough room to store next block */\n    {\n        /* only preserve 64KB within internal buffer. Ensures there is enough room for next block.\n         * note: this situation necessarily implies lastBlockCompressed==fromTmpBuffer */\n        int const realDictSize = LZ4F_localSaveDict(cctxPtr);\n        cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;\n        assert((cctxPtr->tmpIn + blockSize) <= (cctxPtr->tmpBuff + cctxPtr->maxBufferSize));\n    }\n\n    /* some input data left, necessarily < blockSize */\n    if (srcPtr < srcEnd) {\n        /* fill tmp buffer */\n        size_t const sizeToCopy = (size_t)(srcEnd - srcPtr);\n        memcpy(cctxPtr->tmpIn, srcPtr, sizeToCopy);\n        cctxPtr->tmpInSize = sizeToCopy;\n    }\n\n    if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled)\n        (void)XXH32_update(&(cctxPtr->xxh), srcBuffer, srcSize);\n\n    cctxPtr->totalInSize += srcSize;\n    return (size_t)(dstPtr - dstStart);\n}\n\n/*! LZ4F_compressUpdate() :\n *  LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.\n *  When successful, the function always entirely consumes @srcBuffer.\n *  src data is either buffered or compressed into @dstBuffer.\n *  If previously an uncompressed block was written, buffered data is flushed\n *  before appending compressed data is continued.\n * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr).\n * @compressOptionsPtr is optional : provide NULL to mean \"default\".\n * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.\n *           or an error code if it fails (which can be tested using LZ4F_isError())\n *  After an error, the state is left in a UB state, and must be re-initialized.\n */\nsize_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr,\n                           void* dstBuffer, size_t dstCapacity,\n                     const void* srcBuffer, size_t srcSize,\n                     const LZ4F_compressOptions_t* compressOptionsPtr)\n{\n     return LZ4F_compressUpdateImpl(cctxPtr,\n                                   dstBuffer, dstCapacity,\n                                   srcBuffer, srcSize,\n                                   compressOptionsPtr, LZ4B_COMPRESSED);\n}\n\n/*! LZ4F_compressUpdate() :\n *  LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.\n *  When successful, the function always entirely consumes @srcBuffer.\n *  src data is either buffered or compressed into @dstBuffer.\n *  If previously an uncompressed block was written, buffered data is flushed\n *  before appending compressed data is continued.\n *  This is only supported when LZ4F_blockIndependent is used\n * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr).\n * @compressOptionsPtr is optional : provide NULL to mean \"default\".\n * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered.\n *           or an error code if it fails (which can be tested using LZ4F_isError())\n *  After an error, the state is left in a UB state, and must be re-initialized.\n */\nsize_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr,\n                               void* dstBuffer, size_t dstCapacity,\n                         const void* srcBuffer, size_t srcSize,\n                         const LZ4F_compressOptions_t* compressOptionsPtr) {\n    RETURN_ERROR_IF(cctxPtr->prefs.frameInfo.blockMode != LZ4F_blockIndependent, blockMode_invalid);\n    return LZ4F_compressUpdateImpl(cctxPtr,\n                                   dstBuffer, dstCapacity,\n                                   srcBuffer, srcSize,\n                                   compressOptionsPtr, LZ4B_UNCOMPRESSED);\n}\n\n\n/*! LZ4F_flush() :\n *  When compressed data must be sent immediately, without waiting for a block to be filled,\n *  invoke LZ4_flush(), which will immediately compress any remaining data stored within LZ4F_cctx.\n *  The result of the function is the number of bytes written into dstBuffer.\n *  It can be zero, this means there was no data left within LZ4F_cctx.\n *  The function outputs an error code if it fails (can be tested using LZ4F_isError())\n *  LZ4F_compressOptions_t* is optional. NULL is a valid argument.\n */\nsize_t LZ4F_flush(LZ4F_cctx* cctxPtr,\n                  void* dstBuffer, size_t dstCapacity,\n            const LZ4F_compressOptions_t* compressOptionsPtr)\n{\n    BYTE* const dstStart = (BYTE*)dstBuffer;\n    BYTE* dstPtr = dstStart;\n    compressFunc_t compress;\n\n    if (cctxPtr->tmpInSize == 0) return 0;   /* nothing to flush */\n    RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized);\n    RETURN_ERROR_IF(dstCapacity < (cctxPtr->tmpInSize + BHSize + BFSize), dstMaxSize_tooSmall);\n    (void)compressOptionsPtr;   /* not useful (yet) */\n\n    /* select compression function */\n    compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, cctxPtr->blockCompression);\n\n    /* compress tmp buffer */\n    dstPtr += LZ4F_makeBlock(dstPtr,\n                             cctxPtr->tmpIn, cctxPtr->tmpInSize,\n                             compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel,\n                             cctxPtr->cdict,\n                             cctxPtr->prefs.frameInfo.blockChecksumFlag);\n    assert(((void)\"flush overflows dstBuffer!\", (size_t)(dstPtr - dstStart) <= dstCapacity));\n\n    if (cctxPtr->prefs.frameInfo.blockMode == LZ4F_blockLinked)\n        cctxPtr->tmpIn += cctxPtr->tmpInSize;\n    cctxPtr->tmpInSize = 0;\n\n    /* keep tmpIn within limits */\n    if ((cctxPtr->tmpIn + cctxPtr->maxBlockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)) {  /* necessarily LZ4F_blockLinked */\n        int const realDictSize = LZ4F_localSaveDict(cctxPtr);\n        cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize;\n    }\n\n    return (size_t)(dstPtr - dstStart);\n}\n\n\n/*! LZ4F_compressEnd() :\n *  When you want to properly finish the compressed frame, just call LZ4F_compressEnd().\n *  It will flush whatever data remained within compressionContext (like LZ4_flush())\n *  but also properly finalize the frame, with an endMark and an (optional) checksum.\n *  LZ4F_compressOptions_t structure is optional : you can provide NULL as argument.\n * @return: the number of bytes written into dstBuffer (necessarily >= 4 (endMark size))\n *       or an error code if it fails (can be tested using LZ4F_isError())\n *  The context can then be used again to compress a new frame, starting with LZ4F_compressBegin().\n */\nsize_t LZ4F_compressEnd(LZ4F_cctx* cctxPtr,\n                        void* dstBuffer, size_t dstCapacity,\n                  const LZ4F_compressOptions_t* compressOptionsPtr)\n{\n    BYTE* const dstStart = (BYTE*)dstBuffer;\n    BYTE* dstPtr = dstStart;\n\n    size_t const flushSize = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr);\n    DEBUGLOG(5,\"LZ4F_compressEnd: dstCapacity=%u\", (unsigned)dstCapacity);\n    FORWARD_IF_ERROR(flushSize);\n    dstPtr += flushSize;\n\n    assert(flushSize <= dstCapacity);\n    dstCapacity -= flushSize;\n\n    RETURN_ERROR_IF(dstCapacity < 4, dstMaxSize_tooSmall);\n    LZ4F_writeLE32(dstPtr, 0);\n    dstPtr += 4;   /* endMark */\n\n    if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled) {\n        U32 const xxh = XXH32_digest(&(cctxPtr->xxh));\n        RETURN_ERROR_IF(dstCapacity < 8, dstMaxSize_tooSmall);\n        DEBUGLOG(5,\"Writing 32-bit content checksum\");\n        LZ4F_writeLE32(dstPtr, xxh);\n        dstPtr+=4;   /* content Checksum */\n    }\n\n    cctxPtr->cStage = 0;   /* state is now re-usable (with identical preferences) */\n    cctxPtr->maxBufferSize = 0;  /* reuse HC context */\n\n    if (cctxPtr->prefs.frameInfo.contentSize) {\n        if (cctxPtr->prefs.frameInfo.contentSize != cctxPtr->totalInSize)\n            RETURN_ERROR(frameSize_wrong);\n    }\n\n    return (size_t)(dstPtr - dstStart);\n}\n\n\n/*-***************************************************\n*   Frame Decompression\n*****************************************************/\n\ntypedef enum {\n    dstage_getFrameHeader=0, dstage_storeFrameHeader,\n    dstage_init,\n    dstage_getBlockHeader, dstage_storeBlockHeader,\n    dstage_copyDirect, dstage_getBlockChecksum,\n    dstage_getCBlock, dstage_storeCBlock,\n    dstage_flushOut,\n    dstage_getSuffix, dstage_storeSuffix,\n    dstage_getSFrameSize, dstage_storeSFrameSize,\n    dstage_skipSkippable\n} dStage_t;\n\nstruct LZ4F_dctx_s {\n    LZ4F_CustomMem cmem;\n    LZ4F_frameInfo_t frameInfo;\n    U32    version;\n    dStage_t dStage;\n    U64    frameRemainingSize;\n    size_t maxBlockSize;\n    size_t maxBufferSize;\n    BYTE*  tmpIn;\n    size_t tmpInSize;\n    size_t tmpInTarget;\n    BYTE*  tmpOutBuffer;\n    const BYTE* dict;\n    size_t dictSize;\n    BYTE*  tmpOut;\n    size_t tmpOutSize;\n    size_t tmpOutStart;\n    XXH32_state_t xxh;\n    XXH32_state_t blockChecksum;\n    int    skipChecksum;\n    BYTE   header[LZ4F_HEADER_SIZE_MAX];\n};  /* typedef'd to LZ4F_dctx in lz4frame.h */\n\n\nLZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version)\n{\n    LZ4F_dctx* const dctx = (LZ4F_dctx*)LZ4F_calloc(sizeof(LZ4F_dctx), customMem);\n    if (dctx == NULL) return NULL;\n\n    dctx->cmem = customMem;\n    dctx->version = version;\n    return dctx;\n}\n\n/*! LZ4F_createDecompressionContext() :\n *  Create a decompressionContext object, which will track all decompression operations.\n *  Provides a pointer to a fully allocated and initialized LZ4F_decompressionContext object.\n *  Object can later be released using LZ4F_freeDecompressionContext().\n * @return : if != 0, there was an error during context creation.\n */\nLZ4F_errorCode_t\nLZ4F_createDecompressionContext(LZ4F_dctx** LZ4F_decompressionContextPtr, unsigned versionNumber)\n{\n    assert(LZ4F_decompressionContextPtr != NULL);  /* violation of narrow contract */\n    RETURN_ERROR_IF(LZ4F_decompressionContextPtr == NULL, parameter_null);  /* in case it nonetheless happen in production */\n\n    *LZ4F_decompressionContextPtr = LZ4F_createDecompressionContext_advanced(LZ4F_defaultCMem, versionNumber);\n    if (*LZ4F_decompressionContextPtr == NULL) {  /* failed allocation */\n        RETURN_ERROR(allocation_failed);\n    }\n    return LZ4F_OK_NoError;\n}\n\nLZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx)\n{\n    LZ4F_errorCode_t result = LZ4F_OK_NoError;\n    if (dctx != NULL) {   /* can accept NULL input, like free() */\n      result = (LZ4F_errorCode_t)dctx->dStage;\n      LZ4F_free(dctx->tmpIn, dctx->cmem);\n      LZ4F_free(dctx->tmpOutBuffer, dctx->cmem);\n      LZ4F_free(dctx, dctx->cmem);\n    }\n    return result;\n}\n\n\n/*==---   Streaming Decompression operations   ---==*/\n\nvoid LZ4F_resetDecompressionContext(LZ4F_dctx* dctx)\n{\n    dctx->dStage = dstage_getFrameHeader;\n    dctx->dict = NULL;\n    dctx->dictSize = 0;\n    dctx->skipChecksum = 0;\n}\n\n\n/*! LZ4F_decodeHeader() :\n *  input   : `src` points at the **beginning of the frame**\n *  output  : set internal values of dctx, such as\n *            dctx->frameInfo and dctx->dStage.\n *            Also allocates internal buffers.\n *  @return : nb Bytes read from src (necessarily <= srcSize)\n *            or an error code (testable with LZ4F_isError())\n */\nstatic size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize)\n{\n    unsigned blockMode, blockChecksumFlag, contentSizeFlag, contentChecksumFlag, dictIDFlag, blockSizeID;\n    size_t frameHeaderSize;\n    const BYTE* srcPtr = (const BYTE*)src;\n\n    DEBUGLOG(5, \"LZ4F_decodeHeader\");\n    /* need to decode header to get frameInfo */\n    RETURN_ERROR_IF(srcSize < minFHSize, frameHeader_incomplete);   /* minimal frame header size */\n    MEM_INIT(&(dctx->frameInfo), 0, sizeof(dctx->frameInfo));\n\n    /* special case : skippable frames */\n    if ((LZ4F_readLE32(srcPtr) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START) {\n        dctx->frameInfo.frameType = LZ4F_skippableFrame;\n        if (src == (void*)(dctx->header)) {\n            dctx->tmpInSize = srcSize;\n            dctx->tmpInTarget = 8;\n            dctx->dStage = dstage_storeSFrameSize;\n            return srcSize;\n        } else {\n            dctx->dStage = dstage_getSFrameSize;\n            return 4;\n    }   }\n\n    /* control magic number */\n#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION\n    if (LZ4F_readLE32(srcPtr) != LZ4F_MAGICNUMBER) {\n        DEBUGLOG(4, \"frame header error : unknown magic number\");\n        RETURN_ERROR(frameType_unknown);\n    }\n#endif\n    dctx->frameInfo.frameType = LZ4F_frame;\n\n    /* Flags */\n    {   U32 const FLG = srcPtr[4];\n        U32 const version = (FLG>>6) & _2BITS;\n        blockChecksumFlag = (FLG>>4) & _1BIT;\n        blockMode = (FLG>>5) & _1BIT;\n        contentSizeFlag = (FLG>>3) & _1BIT;\n        contentChecksumFlag = (FLG>>2) & _1BIT;\n        dictIDFlag = FLG & _1BIT;\n        /* validate */\n        if (((FLG>>1)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bit */\n        if (version != 1) RETURN_ERROR(headerVersion_wrong);       /* Version Number, only supported value */\n    }\n\n    /* Frame Header Size */\n    frameHeaderSize = minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0);\n\n    if (srcSize < frameHeaderSize) {\n        /* not enough input to fully decode frame header */\n        if (srcPtr != dctx->header)\n            memcpy(dctx->header, srcPtr, srcSize);\n        dctx->tmpInSize = srcSize;\n        dctx->tmpInTarget = frameHeaderSize;\n        dctx->dStage = dstage_storeFrameHeader;\n        return srcSize;\n    }\n\n    {   U32 const BD = srcPtr[5];\n        blockSizeID = (BD>>4) & _3BITS;\n        /* validate */\n        if (((BD>>7)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set);   /* Reserved bit */\n        if (blockSizeID < 4) RETURN_ERROR(maxBlockSize_invalid);    /* 4-7 only supported values for the time being */\n        if (((BD>>0)&_4BITS) != 0) RETURN_ERROR(reservedFlag_set);  /* Reserved bits */\n    }\n\n    /* check header */\n    assert(frameHeaderSize > 5);\n#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION\n    {   BYTE const HC = LZ4F_headerChecksum(srcPtr+4, frameHeaderSize-5);\n        RETURN_ERROR_IF(HC != srcPtr[frameHeaderSize-1], headerChecksum_invalid);\n    }\n#endif\n\n    /* save */\n    dctx->frameInfo.blockMode = (LZ4F_blockMode_t)blockMode;\n    dctx->frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)blockChecksumFlag;\n    dctx->frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)contentChecksumFlag;\n    dctx->frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID;\n    dctx->maxBlockSize = LZ4F_getBlockSize((LZ4F_blockSizeID_t)blockSizeID);\n    if (contentSizeFlag)\n        dctx->frameRemainingSize = dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6);\n    if (dictIDFlag)\n        dctx->frameInfo.dictID = LZ4F_readLE32(srcPtr + frameHeaderSize - 5);\n\n    dctx->dStage = dstage_init;\n\n    return frameHeaderSize;\n}\n\n\n/*! LZ4F_headerSize() :\n * @return : size of frame header\n *           or an error code, which can be tested using LZ4F_isError()\n */\nsize_t LZ4F_headerSize(const void* src, size_t srcSize)\n{\n    RETURN_ERROR_IF(src == NULL, srcPtr_wrong);\n\n    /* minimal srcSize to determine header size */\n    if (srcSize < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH)\n        RETURN_ERROR(frameHeader_incomplete);\n\n    /* special case : skippable frames */\n    if ((LZ4F_readLE32(src) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START)\n        return 8;\n\n    /* control magic number */\n#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION\n    if (LZ4F_readLE32(src) != LZ4F_MAGICNUMBER)\n        RETURN_ERROR(frameType_unknown);\n#endif\n\n    /* Frame Header Size */\n    {   BYTE const FLG = ((const BYTE*)src)[4];\n        U32 const contentSizeFlag = (FLG>>3) & _1BIT;\n        U32 const dictIDFlag = FLG & _1BIT;\n        return minFHSize + (contentSizeFlag?8:0) + (dictIDFlag?4:0);\n    }\n}\n\n/*! LZ4F_getFrameInfo() :\n *  This function extracts frame parameters (max blockSize, frame checksum, etc.).\n *  Usage is optional. Objective is to provide relevant information for allocation purposes.\n *  This function works in 2 situations :\n *   - At the beginning of a new frame, in which case it will decode this information from `srcBuffer`, and start the decoding process.\n *     Amount of input data provided must be large enough to successfully decode the frame header.\n *     A header size is variable, but is guaranteed to be <= LZ4F_HEADER_SIZE_MAX bytes. It's possible to provide more input data than this minimum.\n *   - After decoding has been started. In which case, no input is read, frame parameters are extracted from dctx.\n *  The number of bytes consumed from srcBuffer will be updated within *srcSizePtr (necessarily <= original value).\n *  Decompression must resume from (srcBuffer + *srcSizePtr).\n * @return : an hint about how many srcSize bytes LZ4F_decompress() expects for next call,\n *           or an error code which can be tested using LZ4F_isError()\n *  note 1 : in case of error, dctx is not modified. Decoding operations can resume from where they stopped.\n *  note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure.\n */\nLZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctx,\n                                   LZ4F_frameInfo_t* frameInfoPtr,\n                             const void* srcBuffer, size_t* srcSizePtr)\n{\n    LZ4F_STATIC_ASSERT(dstage_getFrameHeader < dstage_storeFrameHeader);\n    if (dctx->dStage > dstage_storeFrameHeader) {\n        /* frameInfo already decoded */\n        size_t o=0, i=0;\n        *srcSizePtr = 0;\n        *frameInfoPtr = dctx->frameInfo;\n        /* returns : recommended nb of bytes for LZ4F_decompress() */\n        return LZ4F_decompress(dctx, NULL, &o, NULL, &i, NULL);\n    } else {\n        if (dctx->dStage == dstage_storeFrameHeader) {\n            /* frame decoding already started, in the middle of header => automatic fail */\n            *srcSizePtr = 0;\n            RETURN_ERROR(frameDecoding_alreadyStarted);\n        } else {\n            size_t const hSize = LZ4F_headerSize(srcBuffer, *srcSizePtr);\n            if (LZ4F_isError(hSize)) { *srcSizePtr=0; return hSize; }\n            if (*srcSizePtr < hSize) {\n                *srcSizePtr=0;\n                RETURN_ERROR(frameHeader_incomplete);\n            }\n\n            {   size_t decodeResult = LZ4F_decodeHeader(dctx, srcBuffer, hSize);\n                if (LZ4F_isError(decodeResult)) {\n                    *srcSizePtr = 0;\n                } else {\n                    *srcSizePtr = decodeResult;\n                    decodeResult = BHSize;   /* block header size */\n                }\n                *frameInfoPtr = dctx->frameInfo;\n                return decodeResult;\n    }   }   }\n}\n\n\n/* LZ4F_updateDict() :\n * only used for LZ4F_blockLinked mode\n * Condition : @dstPtr != NULL\n */\nstatic void LZ4F_updateDict(LZ4F_dctx* dctx,\n                      const BYTE* dstPtr, size_t dstSize, const BYTE* dstBufferStart,\n                      unsigned withinTmp)\n{\n    assert(dstPtr != NULL);\n    if (dctx->dictSize==0) dctx->dict = (const BYTE*)dstPtr;  /* will lead to prefix mode */\n    assert(dctx->dict != NULL);\n\n    if (dctx->dict + dctx->dictSize == dstPtr) {  /* prefix mode, everything within dstBuffer */\n        dctx->dictSize += dstSize;\n        return;\n    }\n\n    assert(dstPtr >= dstBufferStart);\n    if ((size_t)(dstPtr - dstBufferStart) + dstSize >= 64 KB) {  /* history in dstBuffer becomes large enough to become dictionary */\n        dctx->dict = (const BYTE*)dstBufferStart;\n        dctx->dictSize = (size_t)(dstPtr - dstBufferStart) + dstSize;\n        return;\n    }\n\n    assert(dstSize < 64 KB);   /* if dstSize >= 64 KB, dictionary would be set into dstBuffer directly */\n\n    /* dstBuffer does not contain whole useful history (64 KB), so it must be saved within tmpOutBuffer */\n    assert(dctx->tmpOutBuffer != NULL);\n\n    if (withinTmp && (dctx->dict == dctx->tmpOutBuffer)) {   /* continue history within tmpOutBuffer */\n        /* withinTmp expectation : content of [dstPtr,dstSize] is same as [dict+dictSize,dstSize], so we just extend it */\n        assert(dctx->dict + dctx->dictSize == dctx->tmpOut + dctx->tmpOutStart);\n        dctx->dictSize += dstSize;\n        return;\n    }\n\n    if (withinTmp) { /* copy relevant dict portion in front of tmpOut within tmpOutBuffer */\n        size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer);\n        size_t copySize = 64 KB - dctx->tmpOutSize;\n        const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart;\n        if (dctx->tmpOutSize > 64 KB) copySize = 0;\n        if (copySize > preserveSize) copySize = preserveSize;\n\n        memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);\n\n        dctx->dict = dctx->tmpOutBuffer;\n        dctx->dictSize = preserveSize + dctx->tmpOutStart + dstSize;\n        return;\n    }\n\n    if (dctx->dict == dctx->tmpOutBuffer) {    /* copy dst into tmp to complete dict */\n        if (dctx->dictSize + dstSize > dctx->maxBufferSize) {  /* tmp buffer not large enough */\n            size_t const preserveSize = 64 KB - dstSize;\n            memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);\n            dctx->dictSize = preserveSize;\n        }\n        memcpy(dctx->tmpOutBuffer + dctx->dictSize, dstPtr, dstSize);\n        dctx->dictSize += dstSize;\n        return;\n    }\n\n    /* join dict & dest into tmp */\n    {   size_t preserveSize = 64 KB - dstSize;\n        if (preserveSize > dctx->dictSize) preserveSize = dctx->dictSize;\n        memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - preserveSize, preserveSize);\n        memcpy(dctx->tmpOutBuffer + preserveSize, dstPtr, dstSize);\n        dctx->dict = dctx->tmpOutBuffer;\n        dctx->dictSize = preserveSize + dstSize;\n    }\n}\n\n\n/*! LZ4F_decompress() :\n *  Call this function repetitively to regenerate compressed data in srcBuffer.\n *  The function will attempt to decode up to *srcSizePtr bytes from srcBuffer\n *  into dstBuffer of capacity *dstSizePtr.\n *\n *  The number of bytes regenerated into dstBuffer will be provided within *dstSizePtr (necessarily <= original value).\n *\n *  The number of bytes effectively read from srcBuffer will be provided within *srcSizePtr (necessarily <= original value).\n *  If number of bytes read is < number of bytes provided, then decompression operation is not complete.\n *  Remaining data will have to be presented again in a subsequent invocation.\n *\n *  The function result is an hint of the better srcSize to use for next call to LZ4F_decompress.\n *  Schematically, it's the size of the current (or remaining) compressed block + header of next block.\n *  Respecting the hint provides a small boost to performance, since it allows less buffer shuffling.\n *  Note that this is just a hint, and it's always possible to any srcSize value.\n *  When a frame is fully decoded, @return will be 0.\n *  If decompression failed, @return is an error code which can be tested using LZ4F_isError().\n */\nsize_t LZ4F_decompress(LZ4F_dctx* dctx,\n                       void* dstBuffer, size_t* dstSizePtr,\n                       const void* srcBuffer, size_t* srcSizePtr,\n                       const LZ4F_decompressOptions_t* decompressOptionsPtr)\n{\n    LZ4F_decompressOptions_t optionsNull;\n    const BYTE* const srcStart = (const BYTE*)srcBuffer;\n    const BYTE* const srcEnd = srcStart + *srcSizePtr;\n    const BYTE* srcPtr = srcStart;\n    BYTE* const dstStart = (BYTE*)dstBuffer;\n    BYTE* const dstEnd = dstStart ? dstStart + *dstSizePtr : NULL;\n    BYTE* dstPtr = dstStart;\n    const BYTE* selectedIn = NULL;\n    unsigned doAnotherStage = 1;\n    size_t nextSrcSizeHint = 1;\n\n\n    DEBUGLOG(5, \"LZ4F_decompress : %p,%u => %p,%u\",\n            srcBuffer, (unsigned)*srcSizePtr, dstBuffer, (unsigned)*dstSizePtr);\n    if (dstBuffer == NULL) assert(*dstSizePtr == 0);\n    MEM_INIT(&optionsNull, 0, sizeof(optionsNull));\n    if (decompressOptionsPtr==NULL) decompressOptionsPtr = &optionsNull;\n    *srcSizePtr = 0;\n    *dstSizePtr = 0;\n    assert(dctx != NULL);\n    dctx->skipChecksum |= (decompressOptionsPtr->skipChecksums != 0); /* once set, disable for the remainder of the frame */\n\n    /* behaves as a state machine */\n\n    while (doAnotherStage) {\n\n        switch(dctx->dStage)\n        {\n\n        case dstage_getFrameHeader:\n            DEBUGLOG(6, \"dstage_getFrameHeader\");\n            if ((size_t)(srcEnd-srcPtr) >= maxFHSize) {  /* enough to decode - shortcut */\n                size_t const hSize = LZ4F_decodeHeader(dctx, srcPtr, (size_t)(srcEnd-srcPtr));  /* will update dStage appropriately */\n                FORWARD_IF_ERROR(hSize);\n                srcPtr += hSize;\n                break;\n            }\n            dctx->tmpInSize = 0;\n            if (srcEnd-srcPtr == 0) return minFHSize;   /* 0-size input */\n            dctx->tmpInTarget = minFHSize;   /* minimum size to decode header */\n            dctx->dStage = dstage_storeFrameHeader;\n            /* fall-through */\n\n        case dstage_storeFrameHeader:\n            DEBUGLOG(6, \"dstage_storeFrameHeader\");\n            {   size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize, (size_t)(srcEnd - srcPtr));\n                memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);\n                dctx->tmpInSize += sizeToCopy;\n                srcPtr += sizeToCopy;\n            }\n            if (dctx->tmpInSize < dctx->tmpInTarget) {\n                nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize) + BHSize;   /* rest of header + nextBlockHeader */\n                doAnotherStage = 0;   /* not enough src data, ask for some more */\n                break;\n            }\n            FORWARD_IF_ERROR( LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget) ); /* will update dStage appropriately */\n            break;\n\n        case dstage_init:\n            DEBUGLOG(6, \"dstage_init\");\n            if (dctx->frameInfo.contentChecksumFlag) (void)XXH32_reset(&(dctx->xxh), 0);\n            /* internal buffers allocation */\n            {   size_t const bufferNeeded = dctx->maxBlockSize\n                    + ((dctx->frameInfo.blockMode==LZ4F_blockLinked) ? 128 KB : 0);\n                if (bufferNeeded > dctx->maxBufferSize) {   /* tmp buffers too small */\n                    dctx->maxBufferSize = 0;   /* ensure allocation will be re-attempted on next entry*/\n                    LZ4F_free(dctx->tmpIn, dctx->cmem);\n                    dctx->tmpIn = (BYTE*)LZ4F_malloc(dctx->maxBlockSize + BFSize /* block checksum */, dctx->cmem);\n                    RETURN_ERROR_IF(dctx->tmpIn == NULL, allocation_failed);\n                    LZ4F_free(dctx->tmpOutBuffer, dctx->cmem);\n                    dctx->tmpOutBuffer= (BYTE*)LZ4F_malloc(bufferNeeded, dctx->cmem);\n                    RETURN_ERROR_IF(dctx->tmpOutBuffer== NULL, allocation_failed);\n                    dctx->maxBufferSize = bufferNeeded;\n            }   }\n            dctx->tmpInSize = 0;\n            dctx->tmpInTarget = 0;\n            dctx->tmpOut = dctx->tmpOutBuffer;\n            dctx->tmpOutStart = 0;\n            dctx->tmpOutSize = 0;\n\n            dctx->dStage = dstage_getBlockHeader;\n            /* fall-through */\n\n        case dstage_getBlockHeader:\n            if ((size_t)(srcEnd - srcPtr) >= BHSize) {\n                selectedIn = srcPtr;\n                srcPtr += BHSize;\n            } else {\n                /* not enough input to read cBlockSize field */\n                dctx->tmpInSize = 0;\n                dctx->dStage = dstage_storeBlockHeader;\n            }\n\n            if (dctx->dStage == dstage_storeBlockHeader)   /* can be skipped */\n        case dstage_storeBlockHeader:\n            {   size_t const remainingInput = (size_t)(srcEnd - srcPtr);\n                size_t const wantedData = BHSize - dctx->tmpInSize;\n                size_t const sizeToCopy = MIN(wantedData, remainingInput);\n                memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);\n                srcPtr += sizeToCopy;\n                dctx->tmpInSize += sizeToCopy;\n\n                if (dctx->tmpInSize < BHSize) {   /* not enough input for cBlockSize */\n                    nextSrcSizeHint = BHSize - dctx->tmpInSize;\n                    doAnotherStage  = 0;\n                    break;\n                }\n                selectedIn = dctx->tmpIn;\n            }   /* if (dctx->dStage == dstage_storeBlockHeader) */\n\n        /* decode block header */\n            {   U32 const blockHeader = LZ4F_readLE32(selectedIn);\n                size_t const nextCBlockSize = blockHeader & 0x7FFFFFFFU;\n                size_t const crcSize = dctx->frameInfo.blockChecksumFlag * BFSize;\n                if (blockHeader==0) {  /* frameEnd signal, no more block */\n                    DEBUGLOG(5, \"end of frame\");\n                    dctx->dStage = dstage_getSuffix;\n                    break;\n                }\n                if (nextCBlockSize > dctx->maxBlockSize) {\n                    RETURN_ERROR(maxBlockSize_invalid);\n                }\n                if (blockHeader & LZ4F_BLOCKUNCOMPRESSED_FLAG) {\n                    /* next block is uncompressed */\n                    dctx->tmpInTarget = nextCBlockSize;\n                    DEBUGLOG(5, \"next block is uncompressed (size %u)\", (U32)nextCBlockSize);\n                    if (dctx->frameInfo.blockChecksumFlag) {\n                        (void)XXH32_reset(&dctx->blockChecksum, 0);\n                    }\n                    dctx->dStage = dstage_copyDirect;\n                    break;\n                }\n                /* next block is a compressed block */\n                dctx->tmpInTarget = nextCBlockSize + crcSize;\n                dctx->dStage = dstage_getCBlock;\n                if (dstPtr==dstEnd || srcPtr==srcEnd) {\n                    nextSrcSizeHint = BHSize + nextCBlockSize + crcSize;\n                    doAnotherStage = 0;\n                }\n                break;\n            }\n\n        case dstage_copyDirect:   /* uncompressed block */\n            DEBUGLOG(6, \"dstage_copyDirect\");\n            {   size_t sizeToCopy;\n                if (dstPtr == NULL) {\n                    sizeToCopy = 0;\n                } else {\n                    size_t const minBuffSize = MIN((size_t)(srcEnd-srcPtr), (size_t)(dstEnd-dstPtr));\n                    sizeToCopy = MIN(dctx->tmpInTarget, minBuffSize);\n                    memcpy(dstPtr, srcPtr, sizeToCopy);\n                    if (!dctx->skipChecksum) {\n                        if (dctx->frameInfo.blockChecksumFlag) {\n                            (void)XXH32_update(&dctx->blockChecksum, srcPtr, sizeToCopy);\n                        }\n                        if (dctx->frameInfo.contentChecksumFlag)\n                            (void)XXH32_update(&dctx->xxh, srcPtr, sizeToCopy);\n                    }\n                    if (dctx->frameInfo.contentSize)\n                        dctx->frameRemainingSize -= sizeToCopy;\n\n                    /* history management (linked blocks only)*/\n                    if (dctx->frameInfo.blockMode == LZ4F_blockLinked) {\n                        LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 0);\n                }   }\n\n                srcPtr += sizeToCopy;\n                dstPtr += sizeToCopy;\n                if (sizeToCopy == dctx->tmpInTarget) {   /* all done */\n                    if (dctx->frameInfo.blockChecksumFlag) {\n                        dctx->tmpInSize = 0;\n                        dctx->dStage = dstage_getBlockChecksum;\n                    } else\n                        dctx->dStage = dstage_getBlockHeader;  /* new block */\n                    break;\n                }\n                dctx->tmpInTarget -= sizeToCopy;  /* need to copy more */\n            }\n            nextSrcSizeHint = dctx->tmpInTarget +\n                            +(dctx->frameInfo.blockChecksumFlag ? BFSize : 0)\n                            + BHSize /* next header size */;\n            doAnotherStage = 0;\n            break;\n\n        /* check block checksum for recently transferred uncompressed block */\n        case dstage_getBlockChecksum:\n            DEBUGLOG(6, \"dstage_getBlockChecksum\");\n            {   const void* crcSrc;\n                if ((srcEnd-srcPtr >= 4) && (dctx->tmpInSize==0)) {\n                    crcSrc = srcPtr;\n                    srcPtr += 4;\n                } else {\n                    size_t const stillToCopy = 4 - dctx->tmpInSize;\n                    size_t const sizeToCopy = MIN(stillToCopy, (size_t)(srcEnd-srcPtr));\n                    memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);\n                    dctx->tmpInSize += sizeToCopy;\n                    srcPtr += sizeToCopy;\n                    if (dctx->tmpInSize < 4) {  /* all input consumed */\n                        doAnotherStage = 0;\n                        break;\n                    }\n                    crcSrc = dctx->header;\n                }\n                if (!dctx->skipChecksum) {\n                    U32 const readCRC = LZ4F_readLE32(crcSrc);\n                    U32 const calcCRC = XXH32_digest(&dctx->blockChecksum);\n#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION\n                    DEBUGLOG(6, \"compare block checksum\");\n                    if (readCRC != calcCRC) {\n                        DEBUGLOG(4, \"incorrect block checksum: %08X != %08X\",\n                                readCRC, calcCRC);\n                        RETURN_ERROR(blockChecksum_invalid);\n                    }\n#else\n                    (void)readCRC;\n                    (void)calcCRC;\n#endif\n            }   }\n            dctx->dStage = dstage_getBlockHeader;  /* new block */\n            break;\n\n        case dstage_getCBlock:\n            DEBUGLOG(6, \"dstage_getCBlock\");\n            if ((size_t)(srcEnd-srcPtr) < dctx->tmpInTarget) {\n                dctx->tmpInSize = 0;\n                dctx->dStage = dstage_storeCBlock;\n                break;\n            }\n            /* input large enough to read full block directly */\n            selectedIn = srcPtr;\n            srcPtr += dctx->tmpInTarget;\n\n            if (0)  /* always jump over next block */\n        case dstage_storeCBlock:\n            {   size_t const wantedData = dctx->tmpInTarget - dctx->tmpInSize;\n                size_t const inputLeft = (size_t)(srcEnd-srcPtr);\n                size_t const sizeToCopy = MIN(wantedData, inputLeft);\n                memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);\n                dctx->tmpInSize += sizeToCopy;\n                srcPtr += sizeToCopy;\n                if (dctx->tmpInSize < dctx->tmpInTarget) { /* need more input */\n                    nextSrcSizeHint = (dctx->tmpInTarget - dctx->tmpInSize)\n                                    + (dctx->frameInfo.blockChecksumFlag ? BFSize : 0)\n                                    + BHSize /* next header size */;\n                    doAnotherStage = 0;\n                    break;\n                }\n                selectedIn = dctx->tmpIn;\n            }\n\n            /* At this stage, input is large enough to decode a block */\n\n            /* First, decode and control block checksum if it exists */\n            if (dctx->frameInfo.blockChecksumFlag) {\n                assert(dctx->tmpInTarget >= 4);\n                dctx->tmpInTarget -= 4;\n                assert(selectedIn != NULL);  /* selectedIn is defined at this stage (either srcPtr, or dctx->tmpIn) */\n                {   U32 const readBlockCrc = LZ4F_readLE32(selectedIn + dctx->tmpInTarget);\n                    U32 const calcBlockCrc = XXH32(selectedIn, dctx->tmpInTarget, 0);\n#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION\n                    RETURN_ERROR_IF(readBlockCrc != calcBlockCrc, blockChecksum_invalid);\n#else\n                    (void)readBlockCrc;\n                    (void)calcBlockCrc;\n#endif\n            }   }\n\n            /* decode directly into destination buffer if there is enough room */\n            if ( ((size_t)(dstEnd-dstPtr) >= dctx->maxBlockSize)\n                 /* unless the dictionary is stored in tmpOut:\n                  * in which case it's faster to decode within tmpOut\n                  * to benefit from prefix speedup */\n              && !(dctx->dict!= NULL && (const BYTE*)dctx->dict + dctx->dictSize == dctx->tmpOut) )\n            {\n                const char* dict = (const char*)dctx->dict;\n                size_t dictSize = dctx->dictSize;\n                int decodedSize;\n                assert(dstPtr != NULL);\n                if (dict && dictSize > 1 GB) {\n                    /* overflow control : dctx->dictSize is an int, avoid truncation / sign issues */\n                    dict += dictSize - 64 KB;\n                    dictSize = 64 KB;\n                }\n                decodedSize = LZ4_decompress_safe_usingDict(\n                        (const char*)selectedIn, (char*)dstPtr,\n                        (int)dctx->tmpInTarget, (int)dctx->maxBlockSize,\n                        dict, (int)dictSize);\n                RETURN_ERROR_IF(decodedSize < 0, decompressionFailed);\n                if ((dctx->frameInfo.contentChecksumFlag) && (!dctx->skipChecksum))\n                    XXH32_update(&(dctx->xxh), dstPtr, (size_t)decodedSize);\n                if (dctx->frameInfo.contentSize)\n                    dctx->frameRemainingSize -= (size_t)decodedSize;\n\n                /* dictionary management */\n                if (dctx->frameInfo.blockMode==LZ4F_blockLinked) {\n                    LZ4F_updateDict(dctx, dstPtr, (size_t)decodedSize, dstStart, 0);\n                }\n\n                dstPtr += decodedSize;\n                dctx->dStage = dstage_getBlockHeader;  /* end of block, let's get another one */\n                break;\n            }\n\n            /* not enough place into dst : decode into tmpOut */\n\n            /* manage dictionary */\n            if (dctx->frameInfo.blockMode == LZ4F_blockLinked) {\n                if (dctx->dict == dctx->tmpOutBuffer) {\n                    /* truncate dictionary to 64 KB if too big */\n                    if (dctx->dictSize > 128 KB) {\n                        memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - 64 KB, 64 KB);\n                        dctx->dictSize = 64 KB;\n                    }\n                    dctx->tmpOut = dctx->tmpOutBuffer + dctx->dictSize;\n                } else {  /* dict not within tmpOut */\n                    size_t const reservedDictSpace = MIN(dctx->dictSize, 64 KB);\n                    dctx->tmpOut = dctx->tmpOutBuffer + reservedDictSpace;\n            }   }\n\n            /* Decode block into tmpOut */\n            {   const char* dict = (const char*)dctx->dict;\n                size_t dictSize = dctx->dictSize;\n                int decodedSize;\n                if (dict && dictSize > 1 GB) {\n                    /* the dictSize param is an int, avoid truncation / sign issues */\n                    dict += dictSize - 64 KB;\n                    dictSize = 64 KB;\n                }\n                decodedSize = LZ4_decompress_safe_usingDict(\n                        (const char*)selectedIn, (char*)dctx->tmpOut,\n                        (int)dctx->tmpInTarget, (int)dctx->maxBlockSize,\n                        dict, (int)dictSize);\n                RETURN_ERROR_IF(decodedSize < 0, decompressionFailed);\n                if (dctx->frameInfo.contentChecksumFlag && !dctx->skipChecksum)\n                    XXH32_update(&(dctx->xxh), dctx->tmpOut, (size_t)decodedSize);\n                if (dctx->frameInfo.contentSize)\n                    dctx->frameRemainingSize -= (size_t)decodedSize;\n                dctx->tmpOutSize = (size_t)decodedSize;\n                dctx->tmpOutStart = 0;\n                dctx->dStage = dstage_flushOut;\n            }\n            /* fall-through */\n\n        case dstage_flushOut:  /* flush decoded data from tmpOut to dstBuffer */\n            DEBUGLOG(6, \"dstage_flushOut\");\n            if (dstPtr != NULL) {\n                size_t const sizeToCopy = MIN(dctx->tmpOutSize - dctx->tmpOutStart, (size_t)(dstEnd-dstPtr));\n                memcpy(dstPtr, dctx->tmpOut + dctx->tmpOutStart, sizeToCopy);\n\n                /* dictionary management */\n                if (dctx->frameInfo.blockMode == LZ4F_blockLinked)\n                    LZ4F_updateDict(dctx, dstPtr, sizeToCopy, dstStart, 1 /*withinTmp*/);\n\n                dctx->tmpOutStart += sizeToCopy;\n                dstPtr += sizeToCopy;\n            }\n            if (dctx->tmpOutStart == dctx->tmpOutSize) { /* all flushed */\n                dctx->dStage = dstage_getBlockHeader;  /* get next block */\n                break;\n            }\n            /* could not flush everything : stop there, just request a block header */\n            doAnotherStage = 0;\n            nextSrcSizeHint = BHSize;\n            break;\n\n        case dstage_getSuffix:\n            RETURN_ERROR_IF(dctx->frameRemainingSize, frameSize_wrong);   /* incorrect frame size decoded */\n            if (!dctx->frameInfo.contentChecksumFlag) {  /* no checksum, frame is completed */\n                nextSrcSizeHint = 0;\n                LZ4F_resetDecompressionContext(dctx);\n                doAnotherStage = 0;\n                break;\n            }\n            if ((srcEnd - srcPtr) < 4) {  /* not enough size for entire CRC */\n                dctx->tmpInSize = 0;\n                dctx->dStage = dstage_storeSuffix;\n            } else {\n                selectedIn = srcPtr;\n                srcPtr += 4;\n            }\n\n            if (dctx->dStage == dstage_storeSuffix)   /* can be skipped */\n        case dstage_storeSuffix:\n            {   size_t const remainingInput = (size_t)(srcEnd - srcPtr);\n                size_t const wantedData = 4 - dctx->tmpInSize;\n                size_t const sizeToCopy = MIN(wantedData, remainingInput);\n                memcpy(dctx->tmpIn + dctx->tmpInSize, srcPtr, sizeToCopy);\n                srcPtr += sizeToCopy;\n                dctx->tmpInSize += sizeToCopy;\n                if (dctx->tmpInSize < 4) { /* not enough input to read complete suffix */\n                    nextSrcSizeHint = 4 - dctx->tmpInSize;\n                    doAnotherStage=0;\n                    break;\n                }\n                selectedIn = dctx->tmpIn;\n            }   /* if (dctx->dStage == dstage_storeSuffix) */\n\n        /* case dstage_checkSuffix: */   /* no direct entry, avoid initialization risks */\n            if (!dctx->skipChecksum) {\n                U32 const readCRC = LZ4F_readLE32(selectedIn);\n                U32 const resultCRC = XXH32_digest(&(dctx->xxh));\n#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION\n                RETURN_ERROR_IF(readCRC != resultCRC, contentChecksum_invalid);\n#else\n                (void)readCRC;\n                (void)resultCRC;\n#endif\n            }\n            nextSrcSizeHint = 0;\n            LZ4F_resetDecompressionContext(dctx);\n            doAnotherStage = 0;\n            break;\n\n        case dstage_getSFrameSize:\n            if ((srcEnd - srcPtr) >= 4) {\n                selectedIn = srcPtr;\n                srcPtr += 4;\n            } else {\n                /* not enough input to read cBlockSize field */\n                dctx->tmpInSize = 4;\n                dctx->tmpInTarget = 8;\n                dctx->dStage = dstage_storeSFrameSize;\n            }\n\n            if (dctx->dStage == dstage_storeSFrameSize)\n        case dstage_storeSFrameSize:\n            {   size_t const sizeToCopy = MIN(dctx->tmpInTarget - dctx->tmpInSize,\n                                             (size_t)(srcEnd - srcPtr) );\n                memcpy(dctx->header + dctx->tmpInSize, srcPtr, sizeToCopy);\n                srcPtr += sizeToCopy;\n                dctx->tmpInSize += sizeToCopy;\n                if (dctx->tmpInSize < dctx->tmpInTarget) {\n                    /* not enough input to get full sBlockSize; wait for more */\n                    nextSrcSizeHint = dctx->tmpInTarget - dctx->tmpInSize;\n                    doAnotherStage = 0;\n                    break;\n                }\n                selectedIn = dctx->header + 4;\n            }   /* if (dctx->dStage == dstage_storeSFrameSize) */\n\n        /* case dstage_decodeSFrameSize: */   /* no direct entry */\n            {   size_t const SFrameSize = LZ4F_readLE32(selectedIn);\n                dctx->frameInfo.contentSize = SFrameSize;\n                dctx->tmpInTarget = SFrameSize;\n                dctx->dStage = dstage_skipSkippable;\n                break;\n            }\n\n        case dstage_skipSkippable:\n            {   size_t const skipSize = MIN(dctx->tmpInTarget, (size_t)(srcEnd-srcPtr));\n                srcPtr += skipSize;\n                dctx->tmpInTarget -= skipSize;\n                doAnotherStage = 0;\n                nextSrcSizeHint = dctx->tmpInTarget;\n                if (nextSrcSizeHint) break;  /* still more to skip */\n                /* frame fully skipped : prepare context for a new frame */\n                LZ4F_resetDecompressionContext(dctx);\n                break;\n            }\n        }   /* switch (dctx->dStage) */\n    }   /* while (doAnotherStage) */\n\n    /* preserve history within tmpOut whenever necessary */\n    LZ4F_STATIC_ASSERT((unsigned)dstage_init == 2);\n    if ( (dctx->frameInfo.blockMode==LZ4F_blockLinked)  /* next block will use up to 64KB from previous ones */\n      && (dctx->dict != dctx->tmpOutBuffer)             /* dictionary is not already within tmp */\n      && (dctx->dict != NULL)                           /* dictionary exists */\n      && (!decompressOptionsPtr->stableDst)             /* cannot rely on dst data to remain there for next call */\n      && ((unsigned)(dctx->dStage)-2 < (unsigned)(dstage_getSuffix)-2) )  /* valid stages : [init ... getSuffix[ */\n    {\n        if (dctx->dStage == dstage_flushOut) {\n            size_t const preserveSize = (size_t)(dctx->tmpOut - dctx->tmpOutBuffer);\n            size_t copySize = 64 KB - dctx->tmpOutSize;\n            const BYTE* oldDictEnd = dctx->dict + dctx->dictSize - dctx->tmpOutStart;\n            if (dctx->tmpOutSize > 64 KB) copySize = 0;\n            if (copySize > preserveSize) copySize = preserveSize;\n            assert(dctx->tmpOutBuffer != NULL);\n\n            memcpy(dctx->tmpOutBuffer + preserveSize - copySize, oldDictEnd - copySize, copySize);\n\n            dctx->dict = dctx->tmpOutBuffer;\n            dctx->dictSize = preserveSize + dctx->tmpOutStart;\n        } else {\n            const BYTE* const oldDictEnd = dctx->dict + dctx->dictSize;\n            size_t const newDictSize = MIN(dctx->dictSize, 64 KB);\n\n            memcpy(dctx->tmpOutBuffer, oldDictEnd - newDictSize, newDictSize);\n\n            dctx->dict = dctx->tmpOutBuffer;\n            dctx->dictSize = newDictSize;\n            dctx->tmpOut = dctx->tmpOutBuffer + newDictSize;\n        }\n    }\n\n    *srcSizePtr = (size_t)(srcPtr - srcStart);\n    *dstSizePtr = (size_t)(dstPtr - dstStart);\n    return nextSrcSizeHint;\n}\n\n/*! LZ4F_decompress_usingDict() :\n *  Same as LZ4F_decompress(), using a predefined dictionary.\n *  Dictionary is used \"in place\", without any preprocessing.\n *  It must remain accessible throughout the entire frame decoding.\n */\nsize_t LZ4F_decompress_usingDict(LZ4F_dctx* dctx,\n                       void* dstBuffer, size_t* dstSizePtr,\n                       const void* srcBuffer, size_t* srcSizePtr,\n                       const void* dict, size_t dictSize,\n                       const LZ4F_decompressOptions_t* decompressOptionsPtr)\n{\n    if (dctx->dStage <= dstage_init) {\n        dctx->dict = (const BYTE*)dict;\n        dctx->dictSize = dictSize;\n    }\n    return LZ4F_decompress(dctx, dstBuffer, dstSizePtr,\n                           srcBuffer, srcSizePtr,\n                           decompressOptionsPtr);\n}\n"
  },
  {
    "path": "third-party/l4z/lz4frame.h",
    "content": "/*\n   LZ4F - LZ4-Frame library\n   Header File\n   Copyright (C) 2011-2020, Yann Collet.\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\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\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   You can contact the author at :\n   - LZ4 source repository : https://github.com/lz4/lz4\n   - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c\n*/\n\n/* LZ4F is a stand-alone API able to create and decode LZ4 frames\n * conformant with specification v1.6.1 in doc/lz4_Frame_format.md .\n * Generated frames are compatible with `lz4` CLI.\n *\n * LZ4F also offers streaming capabilities.\n *\n * lz4.h is not required when using lz4frame.h,\n * except to extract common constants such as LZ4_VERSION_NUMBER.\n * */\n\n#ifndef LZ4F_H_09782039843\n#define LZ4F_H_09782039843\n\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\n/* ---   Dependency   --- */\n#include <stddef.h>   /* size_t */\n\n\n/**\n * Introduction\n *\n * lz4frame.h implements LZ4 frame specification: see doc/lz4_Frame_format.md .\n * LZ4 Frames are compatible with `lz4` CLI,\n * and designed to be interoperable with any system.\n**/\n\n/*-***************************************************************\n *  Compiler specifics\n *****************************************************************/\n/*  LZ4_DLL_EXPORT :\n *  Enable exporting of functions when building a Windows DLL\n *  LZ4FLIB_VISIBILITY :\n *  Control library symbols visibility.\n */\n#ifndef LZ4FLIB_VISIBILITY\n#  if defined(__GNUC__) && (__GNUC__ >= 4)\n#    define LZ4FLIB_VISIBILITY __attribute__ ((visibility (\"default\")))\n#  else\n#    define LZ4FLIB_VISIBILITY\n#  endif\n#endif\n#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)\n#  define LZ4FLIB_API __declspec(dllexport) LZ4FLIB_VISIBILITY\n#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)\n#  define LZ4FLIB_API __declspec(dllimport) LZ4FLIB_VISIBILITY\n#else\n#  define LZ4FLIB_API LZ4FLIB_VISIBILITY\n#endif\n\n#ifdef LZ4F_DISABLE_DEPRECATE_WARNINGS\n#  define LZ4F_DEPRECATE(x) x\n#else\n#  if defined(_MSC_VER)\n#    define LZ4F_DEPRECATE(x) x   /* __declspec(deprecated) x - only works with C++ */\n#  elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))\n#    define LZ4F_DEPRECATE(x) x __attribute__((deprecated))\n#  else\n#    define LZ4F_DEPRECATE(x) x   /* no deprecation warning for this compiler */\n#  endif\n#endif\n\n\n/*-************************************\n *  Error management\n **************************************/\ntypedef size_t LZ4F_errorCode_t;\n\nLZ4FLIB_API unsigned    LZ4F_isError(LZ4F_errorCode_t code);   /**< tells when a function result is an error code */\nLZ4FLIB_API const char* LZ4F_getErrorName(LZ4F_errorCode_t code);   /**< return error code string; for debugging */\n\n\n/*-************************************\n *  Frame compression types\n ************************************* */\n/* #define LZ4F_ENABLE_OBSOLETE_ENUMS   // uncomment to enable obsolete enums */\n#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS\n#  define LZ4F_OBSOLETE_ENUM(x) , LZ4F_DEPRECATE(x) = LZ4F_##x\n#else\n#  define LZ4F_OBSOLETE_ENUM(x)\n#endif\n\n/* The larger the block size, the (slightly) better the compression ratio,\n * though there are diminishing returns.\n * Larger blocks also increase memory usage on both compression and decompression sides.\n */\ntypedef enum {\n    LZ4F_default=0,\n    LZ4F_max64KB=4,\n    LZ4F_max256KB=5,\n    LZ4F_max1MB=6,\n    LZ4F_max4MB=7\n    LZ4F_OBSOLETE_ENUM(max64KB)\n    LZ4F_OBSOLETE_ENUM(max256KB)\n    LZ4F_OBSOLETE_ENUM(max1MB)\n    LZ4F_OBSOLETE_ENUM(max4MB)\n} LZ4F_blockSizeID_t;\n\n/* Linked blocks sharply reduce inefficiencies when using small blocks,\n * they compress better.\n * However, some LZ4 decoders are only compatible with independent blocks */\ntypedef enum {\n    LZ4F_blockLinked=0,\n    LZ4F_blockIndependent\n    LZ4F_OBSOLETE_ENUM(blockLinked)\n    LZ4F_OBSOLETE_ENUM(blockIndependent)\n} LZ4F_blockMode_t;\n\ntypedef enum {\n    LZ4F_noContentChecksum=0,\n    LZ4F_contentChecksumEnabled\n    LZ4F_OBSOLETE_ENUM(noContentChecksum)\n    LZ4F_OBSOLETE_ENUM(contentChecksumEnabled)\n} LZ4F_contentChecksum_t;\n\ntypedef enum {\n    LZ4F_noBlockChecksum=0,\n    LZ4F_blockChecksumEnabled\n} LZ4F_blockChecksum_t;\n\ntypedef enum {\n    LZ4F_frame=0,\n    LZ4F_skippableFrame\n    LZ4F_OBSOLETE_ENUM(skippableFrame)\n} LZ4F_frameType_t;\n\n#ifdef LZ4F_ENABLE_OBSOLETE_ENUMS\ntypedef LZ4F_blockSizeID_t blockSizeID_t;\ntypedef LZ4F_blockMode_t blockMode_t;\ntypedef LZ4F_frameType_t frameType_t;\ntypedef LZ4F_contentChecksum_t contentChecksum_t;\n#endif\n\n/*! LZ4F_frameInfo_t :\n *  makes it possible to set or read frame parameters.\n *  Structure must be first init to 0, using memset() or LZ4F_INIT_FRAMEINFO,\n *  setting all parameters to default.\n *  It's then possible to update selectively some parameters */\ntypedef struct {\n  LZ4F_blockSizeID_t     blockSizeID;         /* max64KB, max256KB, max1MB, max4MB; 0 == default */\n  LZ4F_blockMode_t       blockMode;           /* LZ4F_blockLinked, LZ4F_blockIndependent; 0 == default */\n  LZ4F_contentChecksum_t contentChecksumFlag; /* 1: frame terminated with 32-bit checksum of decompressed data; 0: disabled (default) */\n  LZ4F_frameType_t       frameType;           /* read-only field : LZ4F_frame or LZ4F_skippableFrame */\n  unsigned long long     contentSize;         /* Size of uncompressed content ; 0 == unknown */\n  unsigned               dictID;              /* Dictionary ID, sent by compressor to help decoder select correct dictionary; 0 == no dictID provided */\n  LZ4F_blockChecksum_t   blockChecksumFlag;   /* 1: each block followed by a checksum of block's compressed data; 0: disabled (default) */\n} LZ4F_frameInfo_t;\n\n#define LZ4F_INIT_FRAMEINFO   { LZ4F_default, LZ4F_blockLinked, LZ4F_noContentChecksum, LZ4F_frame, 0ULL, 0U, LZ4F_noBlockChecksum }    /* v1.8.3+ */\n\n/*! LZ4F_preferences_t :\n *  makes it possible to supply advanced compression instructions to streaming interface.\n *  Structure must be first init to 0, using memset() or LZ4F_INIT_PREFERENCES,\n *  setting all parameters to default.\n *  All reserved fields must be set to zero. */\ntypedef struct {\n  LZ4F_frameInfo_t frameInfo;\n  int      compressionLevel;    /* 0: default (fast mode); values > LZ4HC_CLEVEL_MAX count as LZ4HC_CLEVEL_MAX; values < 0 trigger \"fast acceleration\" */\n  unsigned autoFlush;           /* 1: always flush; reduces usage of internal buffers */\n  unsigned favorDecSpeed;       /* 1: parser favors decompression speed vs compression ratio. Only works for high compression modes (>= LZ4HC_CLEVEL_OPT_MIN) */  /* v1.8.2+ */\n  unsigned reserved[3];         /* must be zero for forward compatibility */\n} LZ4F_preferences_t;\n\n#define LZ4F_INIT_PREFERENCES   { LZ4F_INIT_FRAMEINFO, 0, 0u, 0u, { 0u, 0u, 0u } }    /* v1.8.3+ */\n\n\n/*-*********************************\n*  Simple compression function\n***********************************/\n\nLZ4FLIB_API int LZ4F_compressionLevel_max(void);   /* v1.8.0+ */\n\n/*! LZ4F_compressFrameBound() :\n *  Returns the maximum possible compressed size with LZ4F_compressFrame() given srcSize and preferences.\n * `preferencesPtr` is optional. It can be replaced by NULL, in which case, the function will assume default preferences.\n *  Note : this result is only usable with LZ4F_compressFrame().\n *         It may also be relevant to LZ4F_compressUpdate() _only if_ no flush() operation is ever performed.\n */\nLZ4FLIB_API size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr);\n\n/*! LZ4F_compressFrame() :\n *  Compress an entire srcBuffer into a valid LZ4 frame.\n *  dstCapacity MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).\n *  The LZ4F_preferences_t structure is optional : you can provide NULL as argument. All preferences will be set to default.\n * @return : number of bytes written into dstBuffer.\n *           or an error code if it fails (can be tested using LZ4F_isError())\n */\nLZ4FLIB_API size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity,\n                                const void* srcBuffer, size_t srcSize,\n                                const LZ4F_preferences_t* preferencesPtr);\n\n\n/*-***********************************\n*  Advanced compression functions\n*************************************/\ntypedef struct LZ4F_cctx_s LZ4F_cctx;   /* incomplete type */\ntypedef LZ4F_cctx* LZ4F_compressionContext_t;  /* for compatibility with older APIs, prefer using LZ4F_cctx */\n\ntypedef struct {\n  unsigned stableSrc;    /* 1 == src content will remain present on future calls to LZ4F_compress(); skip copying src content within tmp buffer */\n  unsigned reserved[3];\n} LZ4F_compressOptions_t;\n\n/*---   Resource Management   ---*/\n\n#define LZ4F_VERSION 100    /* This number can be used to check for an incompatible API breaking change */\nLZ4FLIB_API unsigned LZ4F_getVersion(void);\n\n/*! LZ4F_createCompressionContext() :\n *  The first thing to do is to create a compressionContext object,\n *  which will keep track of operation state during streaming compression.\n *  This is achieved using LZ4F_createCompressionContext(), which takes as argument a version,\n *  and a pointer to LZ4F_cctx*, to write the resulting pointer into.\n *  @version provided MUST be LZ4F_VERSION. It is intended to track potential version mismatch, notably when using DLL.\n *  The function provides a pointer to a fully allocated LZ4F_cctx object.\n *  @cctxPtr MUST be != NULL.\n *  If @return != zero, context creation failed.\n *  A created compression context can be employed multiple times for consecutive streaming operations.\n *  Once all streaming compression jobs are completed,\n *  the state object can be released using LZ4F_freeCompressionContext().\n *  Note1 : LZ4F_freeCompressionContext() is always successful. Its return value can be ignored.\n *  Note2 : LZ4F_freeCompressionContext() works fine with NULL input pointers (do nothing).\n**/\nLZ4FLIB_API LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_cctx** cctxPtr, unsigned version);\nLZ4FLIB_API LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctx);\n\n\n/*----    Compression    ----*/\n\n#define LZ4F_HEADER_SIZE_MIN  7   /* LZ4 Frame header size can vary, depending on selected parameters */\n#define LZ4F_HEADER_SIZE_MAX 19\n\n/* Size in bytes of a block header in little-endian format. Highest bit indicates if block data is uncompressed */\n#define LZ4F_BLOCK_HEADER_SIZE 4\n\n/* Size in bytes of a block checksum footer in little-endian format. */\n#define LZ4F_BLOCK_CHECKSUM_SIZE 4\n\n/* Size in bytes of the content checksum. */\n#define LZ4F_CONTENT_CHECKSUM_SIZE 4\n\n/*! LZ4F_compressBegin() :\n *  will write the frame header into dstBuffer.\n *  dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.\n * `prefsPtr` is optional : you can provide NULL as argument, all preferences will then be set to default.\n * @return : number of bytes written into dstBuffer for the header\n *           or an error code (which can be tested using LZ4F_isError())\n */\nLZ4FLIB_API size_t LZ4F_compressBegin(LZ4F_cctx* cctx,\n                                      void* dstBuffer, size_t dstCapacity,\n                                      const LZ4F_preferences_t* prefsPtr);\n\n/*! LZ4F_compressBound() :\n *  Provides minimum dstCapacity required to guarantee success of\n *  LZ4F_compressUpdate(), given a srcSize and preferences, for a worst case scenario.\n *  When srcSize==0, LZ4F_compressBound() provides an upper bound for LZ4F_flush() and LZ4F_compressEnd() instead.\n *  Note that the result is only valid for a single invocation of LZ4F_compressUpdate().\n *  When invoking LZ4F_compressUpdate() multiple times,\n *  if the output buffer is gradually filled up instead of emptied and re-used from its start,\n *  one must check if there is enough remaining capacity before each invocation, using LZ4F_compressBound().\n * @return is always the same for a srcSize and prefsPtr.\n *  prefsPtr is optional : when NULL is provided, preferences will be set to cover worst case scenario.\n *  tech details :\n * @return if automatic flushing is not enabled, includes the possibility that internal buffer might already be filled by up to (blockSize-1) bytes.\n *  It also includes frame footer (ending + checksum), since it might be generated by LZ4F_compressEnd().\n * @return doesn't include frame header, as it was already generated by LZ4F_compressBegin().\n */\nLZ4FLIB_API size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* prefsPtr);\n\n/*! LZ4F_compressUpdate() :\n *  LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary.\n *  Important rule: dstCapacity MUST be large enough to ensure operation success even in worst case situations.\n *  This value is provided by LZ4F_compressBound().\n *  If this condition is not respected, LZ4F_compress() will fail (result is an errorCode).\n *  After an error, the state is left in a UB state, and must be re-initialized or freed.\n *  If previously an uncompressed block was written, buffered data is flushed\n *  before appending compressed data is continued.\n * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default.\n * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered).\n *           or an error code if it fails (which can be tested using LZ4F_isError())\n */\nLZ4FLIB_API size_t LZ4F_compressUpdate(LZ4F_cctx* cctx,\n                                       void* dstBuffer, size_t dstCapacity,\n                                 const void* srcBuffer, size_t srcSize,\n                                 const LZ4F_compressOptions_t* cOptPtr);\n\n/*! LZ4F_flush() :\n *  When data must be generated and sent immediately, without waiting for a block to be completely filled,\n *  it's possible to call LZ4_flush(). It will immediately compress any data buffered within cctx.\n * `dstCapacity` must be large enough to ensure the operation will be successful.\n * `cOptPtr` is optional : it's possible to provide NULL, all options will be set to default.\n * @return : nb of bytes written into dstBuffer (can be zero, when there is no data stored within cctx)\n *           or an error code if it fails (which can be tested using LZ4F_isError())\n *  Note : LZ4F_flush() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr).\n */\nLZ4FLIB_API size_t LZ4F_flush(LZ4F_cctx* cctx,\n                              void* dstBuffer, size_t dstCapacity,\n                        const LZ4F_compressOptions_t* cOptPtr);\n\n/*! LZ4F_compressEnd() :\n *  To properly finish an LZ4 frame, invoke LZ4F_compressEnd().\n *  It will flush whatever data remained within `cctx` (like LZ4_flush())\n *  and properly finalize the frame, with an endMark and a checksum.\n * `cOptPtr` is optional : NULL can be provided, in which case all options will be set to default.\n * @return : nb of bytes written into dstBuffer, necessarily >= 4 (endMark),\n *           or an error code if it fails (which can be tested using LZ4F_isError())\n *  Note : LZ4F_compressEnd() is guaranteed to be successful when dstCapacity >= LZ4F_compressBound(0, prefsPtr).\n *  A successful call to LZ4F_compressEnd() makes `cctx` available again for another compression task.\n */\nLZ4FLIB_API size_t LZ4F_compressEnd(LZ4F_cctx* cctx,\n                                    void* dstBuffer, size_t dstCapacity,\n                              const LZ4F_compressOptions_t* cOptPtr);\n\n\n/*-*********************************\n*  Decompression functions\n***********************************/\ntypedef struct LZ4F_dctx_s LZ4F_dctx;   /* incomplete type */\ntypedef LZ4F_dctx* LZ4F_decompressionContext_t;   /* compatibility with previous API versions */\n\ntypedef struct {\n  unsigned stableDst;     /* pledges that last 64KB decompressed data will remain available unmodified between invocations.\n                           * This optimization skips storage operations in tmp buffers. */\n  unsigned skipChecksums; /* disable checksum calculation and verification, even when one is present in frame, to save CPU time.\n                           * Setting this option to 1 once disables all checksums for the rest of the frame. */\n  unsigned reserved1;     /* must be set to zero for forward compatibility */\n  unsigned reserved0;     /* idem */\n} LZ4F_decompressOptions_t;\n\n\n/* Resource management */\n\n/*! LZ4F_createDecompressionContext() :\n *  Create an LZ4F_dctx object, to track all decompression operations.\n *  @version provided MUST be LZ4F_VERSION.\n *  @dctxPtr MUST be valid.\n *  The function fills @dctxPtr with the value of a pointer to an allocated and initialized LZ4F_dctx object.\n *  The @return is an errorCode, which can be tested using LZ4F_isError().\n *  dctx memory can be released using LZ4F_freeDecompressionContext();\n *  Result of LZ4F_freeDecompressionContext() indicates current state of decompressionContext when being released.\n *  That is, it should be == 0 if decompression has been completed fully and correctly.\n */\nLZ4FLIB_API LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_dctx** dctxPtr, unsigned version);\nLZ4FLIB_API LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx);\n\n\n/*-***********************************\n*  Streaming decompression functions\n*************************************/\n\n#define LZ4F_MAGICNUMBER 0x184D2204U\n#define LZ4F_MAGIC_SKIPPABLE_START 0x184D2A50U\n#define LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH 5\n\n/*! LZ4F_headerSize() : v1.9.0+\n *  Provide the header size of a frame starting at `src`.\n * `srcSize` must be >= LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH,\n *  which is enough to decode the header length.\n * @return : size of frame header\n *           or an error code, which can be tested using LZ4F_isError()\n *  note : Frame header size is variable, but is guaranteed to be\n *         >= LZ4F_HEADER_SIZE_MIN bytes, and <= LZ4F_HEADER_SIZE_MAX bytes.\n */\nLZ4FLIB_API size_t LZ4F_headerSize(const void* src, size_t srcSize);\n\n/*! LZ4F_getFrameInfo() :\n *  This function extracts frame parameters (max blockSize, dictID, etc.).\n *  Its usage is optional: user can also invoke LZ4F_decompress() directly.\n *\n *  Extracted information will fill an existing LZ4F_frameInfo_t structure.\n *  This can be useful for allocation and dictionary identification purposes.\n *\n *  LZ4F_getFrameInfo() can work in the following situations :\n *\n *  1) At the beginning of a new frame, before any invocation of LZ4F_decompress().\n *     It will decode header from `srcBuffer`,\n *     consuming the header and starting the decoding process.\n *\n *     Input size must be large enough to contain the full frame header.\n *     Frame header size can be known beforehand by LZ4F_headerSize().\n *     Frame header size is variable, but is guaranteed to be >= LZ4F_HEADER_SIZE_MIN bytes,\n *     and not more than <= LZ4F_HEADER_SIZE_MAX bytes.\n *     Hence, blindly providing LZ4F_HEADER_SIZE_MAX bytes or more will always work.\n *     It's allowed to provide more input data than the header size,\n *     LZ4F_getFrameInfo() will only consume the header.\n *\n *     If input size is not large enough,\n *     aka if it's smaller than header size,\n *     function will fail and return an error code.\n *\n *  2) After decoding has been started,\n *     it's possible to invoke LZ4F_getFrameInfo() anytime\n *     to extract already decoded frame parameters stored within dctx.\n *\n *     Note that, if decoding has barely started,\n *     and not yet read enough information to decode the header,\n *     LZ4F_getFrameInfo() will fail.\n *\n *  The number of bytes consumed from srcBuffer will be updated in *srcSizePtr (necessarily <= original value).\n *  LZ4F_getFrameInfo() only consumes bytes when decoding has not yet started,\n *  and when decoding the header has been successful.\n *  Decompression must then resume from (srcBuffer + *srcSizePtr).\n *\n * @return : a hint about how many srcSize bytes LZ4F_decompress() expects for next call,\n *           or an error code which can be tested using LZ4F_isError().\n *  note 1 : in case of error, dctx is not modified. Decoding operation can resume from beginning safely.\n *  note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure.\n */\nLZ4FLIB_API size_t\nLZ4F_getFrameInfo(LZ4F_dctx* dctx,\n                  LZ4F_frameInfo_t* frameInfoPtr,\n            const void* srcBuffer, size_t* srcSizePtr);\n\n/*! LZ4F_decompress() :\n *  Call this function repetitively to regenerate data compressed in `srcBuffer`.\n *\n *  The function requires a valid dctx state.\n *  It will read up to *srcSizePtr bytes from srcBuffer,\n *  and decompress data into dstBuffer, of capacity *dstSizePtr.\n *\n *  The nb of bytes consumed from srcBuffer will be written into *srcSizePtr (necessarily <= original value).\n *  The nb of bytes decompressed into dstBuffer will be written into *dstSizePtr (necessarily <= original value).\n *\n *  The function does not necessarily read all input bytes, so always check value in *srcSizePtr.\n *  Unconsumed source data must be presented again in subsequent invocations.\n *\n * `dstBuffer` can freely change between each consecutive function invocation.\n * `dstBuffer` content will be overwritten.\n *\n * @return : an hint of how many `srcSize` bytes LZ4F_decompress() expects for next call.\n *  Schematically, it's the size of the current (or remaining) compressed block + header of next block.\n *  Respecting the hint provides some small speed benefit, because it skips intermediate buffers.\n *  This is just a hint though, it's always possible to provide any srcSize.\n *\n *  When a frame is fully decoded, @return will be 0 (no more data expected).\n *  When provided with more bytes than necessary to decode a frame,\n *  LZ4F_decompress() will stop reading exactly at end of current frame, and @return 0.\n *\n *  If decompression failed, @return is an error code, which can be tested using LZ4F_isError().\n *  After a decompression error, the `dctx` context is not resumable.\n *  Use LZ4F_resetDecompressionContext() to return to clean state.\n *\n *  After a frame is fully decoded, dctx can be used again to decompress another frame.\n */\nLZ4FLIB_API size_t\nLZ4F_decompress(LZ4F_dctx* dctx,\n                void* dstBuffer, size_t* dstSizePtr,\n          const void* srcBuffer, size_t* srcSizePtr,\n          const LZ4F_decompressOptions_t* dOptPtr);\n\n\n/*! LZ4F_resetDecompressionContext() : added in v1.8.0\n *  In case of an error, the context is left in \"undefined\" state.\n *  In which case, it's necessary to reset it, before re-using it.\n *  This method can also be used to abruptly stop any unfinished decompression,\n *  and start a new one using same context resources. */\nLZ4FLIB_API void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx);   /* always successful */\n\n\n\n#if defined (__cplusplus)\n}\n#endif\n\n#endif  /* LZ4F_H_09782039843 */\n\n#if defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843)\n#define LZ4F_H_STATIC_09782039843\n\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\n/* These declarations are not stable and may change in the future.\n * They are therefore only safe to depend on\n * when the caller is statically linked against the library.\n * To access their declarations, define LZ4F_STATIC_LINKING_ONLY.\n *\n * By default, these symbols aren't published into shared/dynamic libraries.\n * You can override this behavior and force them to be published\n * by defining LZ4F_PUBLISH_STATIC_FUNCTIONS.\n * Use at your own risk.\n */\n#ifdef LZ4F_PUBLISH_STATIC_FUNCTIONS\n# define LZ4FLIB_STATIC_API LZ4FLIB_API\n#else\n# define LZ4FLIB_STATIC_API\n#endif\n\n\n/* ---   Error List   --- */\n#define LZ4F_LIST_ERRORS(ITEM) \\\n        ITEM(OK_NoError) \\\n        ITEM(ERROR_GENERIC) \\\n        ITEM(ERROR_maxBlockSize_invalid) \\\n        ITEM(ERROR_blockMode_invalid) \\\n        ITEM(ERROR_contentChecksumFlag_invalid) \\\n        ITEM(ERROR_compressionLevel_invalid) \\\n        ITEM(ERROR_headerVersion_wrong) \\\n        ITEM(ERROR_blockChecksum_invalid) \\\n        ITEM(ERROR_reservedFlag_set) \\\n        ITEM(ERROR_allocation_failed) \\\n        ITEM(ERROR_srcSize_tooLarge) \\\n        ITEM(ERROR_dstMaxSize_tooSmall) \\\n        ITEM(ERROR_frameHeader_incomplete) \\\n        ITEM(ERROR_frameType_unknown) \\\n        ITEM(ERROR_frameSize_wrong) \\\n        ITEM(ERROR_srcPtr_wrong) \\\n        ITEM(ERROR_decompressionFailed) \\\n        ITEM(ERROR_headerChecksum_invalid) \\\n        ITEM(ERROR_contentChecksum_invalid) \\\n        ITEM(ERROR_frameDecoding_alreadyStarted) \\\n        ITEM(ERROR_compressionState_uninitialized) \\\n        ITEM(ERROR_parameter_null) \\\n        ITEM(ERROR_maxCode)\n\n#define LZ4F_GENERATE_ENUM(ENUM) LZ4F_##ENUM,\n\n/* enum list is exposed, to handle specific errors */\ntypedef enum { LZ4F_LIST_ERRORS(LZ4F_GENERATE_ENUM)\n              _LZ4F_dummy_error_enum_for_c89_never_used } LZ4F_errorCodes;\n\nLZ4FLIB_STATIC_API LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult);\n\n\n/*! LZ4F_getBlockSize() :\n *  Return, in scalar format (size_t),\n *  the maximum block size associated with blockSizeID.\n**/\nLZ4FLIB_STATIC_API size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID);\n\n/*! LZ4F_uncompressedUpdate() :\n *  LZ4F_uncompressedUpdate() can be called repetitively to add as much data uncompressed data as necessary.\n *  Important rule: dstCapacity MUST be large enough to store the entire source buffer as\n *  no compression is done for this operation\n *  If this condition is not respected, LZ4F_uncompressedUpdate() will fail (result is an errorCode).\n *  After an error, the state is left in a UB state, and must be re-initialized or freed.\n *  If previously a compressed block was written, buffered data is flushed\n *  before appending uncompressed data is continued.\n *  This is only supported when LZ4F_blockIndependent is used\n * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default.\n * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered).\n *           or an error code if it fails (which can be tested using LZ4F_isError())\n */\nLZ4FLIB_STATIC_API size_t\nLZ4F_uncompressedUpdate(LZ4F_cctx* cctx,\n                        void* dstBuffer, size_t dstCapacity,\n                  const void* srcBuffer, size_t srcSize,\n                  const LZ4F_compressOptions_t* cOptPtr);\n\n/**********************************\n *  Bulk processing dictionary API\n *********************************/\n\n/* A Dictionary is useful for the compression of small messages (KB range).\n * It dramatically improves compression efficiency.\n *\n * LZ4 can ingest any input as dictionary, though only the last 64 KB are useful.\n * Best results are generally achieved by using Zstandard's Dictionary Builder\n * to generate a high-quality dictionary from a set of samples.\n *\n * Loading a dictionary has a cost, since it involves construction of tables.\n * The Bulk processing dictionary API makes it possible to share this cost\n * over an arbitrary number of compression jobs, even concurrently,\n * markedly improving compression latency for these cases.\n *\n * The same dictionary will have to be used on the decompression side\n * for decoding to be successful.\n * To help identify the correct dictionary at decoding stage,\n * the frame header allows optional embedding of a dictID field.\n */\ntypedef struct LZ4F_CDict_s LZ4F_CDict;\n\n/*! LZ4_createCDict() :\n *  When compressing multiple messages / blocks using the same dictionary, it's recommended to load it just once.\n *  LZ4_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay.\n *  LZ4_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only.\n * `dictBuffer` can be released after LZ4_CDict creation, since its content is copied within CDict */\nLZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize);\nLZ4FLIB_STATIC_API void        LZ4F_freeCDict(LZ4F_CDict* CDict);\n\n\n/*! LZ4_compressFrame_usingCDict() :\n *  Compress an entire srcBuffer into a valid LZ4 frame using a digested Dictionary.\n *  cctx must point to a context created by LZ4F_createCompressionContext().\n *  If cdict==NULL, compress without a dictionary.\n *  dstBuffer MUST be >= LZ4F_compressFrameBound(srcSize, preferencesPtr).\n *  If this condition is not respected, function will fail (@return an errorCode).\n *  The LZ4F_preferences_t structure is optional : you may provide NULL as argument,\n *  but it's not recommended, as it's the only way to provide dictID in the frame header.\n * @return : number of bytes written into dstBuffer.\n *           or an error code if it fails (can be tested using LZ4F_isError()) */\nLZ4FLIB_STATIC_API size_t\nLZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx,\n                              void* dst, size_t dstCapacity,\n                        const void* src, size_t srcSize,\n                        const LZ4F_CDict* cdict,\n                        const LZ4F_preferences_t* preferencesPtr);\n\n\n/*! LZ4F_compressBegin_usingCDict() :\n *  Inits streaming dictionary compression, and writes the frame header into dstBuffer.\n *  dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes.\n * `prefsPtr` is optional : you may provide NULL as argument,\n *  however, it's the only way to provide dictID in the frame header.\n * @return : number of bytes written into dstBuffer for the header,\n *           or an error code (which can be tested using LZ4F_isError()) */\nLZ4FLIB_STATIC_API size_t\nLZ4F_compressBegin_usingCDict(LZ4F_cctx* cctx,\n                              void* dstBuffer, size_t dstCapacity,\n                        const LZ4F_CDict* cdict,\n                        const LZ4F_preferences_t* prefsPtr);\n\n\n/*! LZ4F_decompress_usingDict() :\n *  Same as LZ4F_decompress(), using a predefined dictionary.\n *  Dictionary is used \"in place\", without any preprocessing.\n**  It must remain accessible throughout the entire frame decoding. */\nLZ4FLIB_STATIC_API size_t\nLZ4F_decompress_usingDict(LZ4F_dctx* dctxPtr,\n                          void* dstBuffer, size_t* dstSizePtr,\n                    const void* srcBuffer, size_t* srcSizePtr,\n                    const void* dict, size_t dictSize,\n                    const LZ4F_decompressOptions_t* decompressOptionsPtr);\n\n\n/*! Custom memory allocation :\n *  These prototypes make it possible to pass custom allocation/free functions.\n *  LZ4F_customMem is provided at state creation time, using LZ4F_create*_advanced() listed below.\n *  All allocation/free operations will be completed using these custom variants instead of regular <stdlib.h> ones.\n */\ntypedef void* (*LZ4F_AllocFunction) (void* opaqueState, size_t size);\ntypedef void* (*LZ4F_CallocFunction) (void* opaqueState, size_t size);\ntypedef void  (*LZ4F_FreeFunction) (void* opaqueState, void* address);\ntypedef struct {\n    LZ4F_AllocFunction customAlloc;\n    LZ4F_CallocFunction customCalloc; /* optional; when not defined, uses customAlloc + memset */\n    LZ4F_FreeFunction customFree;\n    void* opaqueState;\n} LZ4F_CustomMem;\nstatic\n#ifdef __GNUC__\n__attribute__((__unused__))\n#endif\nLZ4F_CustomMem const LZ4F_defaultCMem = { NULL, NULL, NULL, NULL };  /**< this constant defers to stdlib's functions */\n\nLZ4FLIB_STATIC_API LZ4F_cctx* LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version);\nLZ4FLIB_STATIC_API LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version);\nLZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict_advanced(LZ4F_CustomMem customMem, const void* dictBuffer, size_t dictSize);\n\n\n#if defined (__cplusplus)\n}\n#endif\n\n#endif  /* defined(LZ4F_STATIC_LINKING_ONLY) && !defined(LZ4F_H_STATIC_09782039843) */\n"
  },
  {
    "path": "third-party/l4z/lz4frame_static.h",
    "content": "/*\n   LZ4 auto-framing library\n   Header File for static linking only\n   Copyright (C) 2011-2020, Yann Collet.\n\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\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\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   You can contact the author at :\n   - LZ4 source repository : https://github.com/lz4/lz4\n   - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c\n*/\n\n#ifndef LZ4FRAME_STATIC_H_0398209384\n#define LZ4FRAME_STATIC_H_0398209384\n\n/* The declarations that formerly were made here have been merged into\n * lz4frame.h, protected by the LZ4F_STATIC_LINKING_ONLY macro. Going forward,\n * it is recommended to simply include that header directly.\n */\n\n#define LZ4F_STATIC_LINKING_ONLY\n#include \"lz4frame.h\"\n\n#endif /* LZ4FRAME_STATIC_H_0398209384 */\n"
  },
  {
    "path": "third-party/l4z/lz4hc.c",
    "content": "/*\n    LZ4 HC - High Compression Mode of LZ4\n    Copyright (C) 2011-2020, Yann Collet.\n\n    BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\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\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    You can contact the author at :\n       - LZ4 source repository : https://github.com/lz4/lz4\n       - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c\n*/\n/* note : lz4hc is not an independent module, it requires lz4.h/lz4.c for proper compilation */\n\n\n/* *************************************\n*  Tuning Parameter\n***************************************/\n\n/*! HEAPMODE :\n *  Select how default compression function will allocate workplace memory,\n *  in stack (0:fastest), or in heap (1:requires malloc()).\n *  Since workplace is rather large, heap mode is recommended.\n**/\n#ifndef LZ4HC_HEAPMODE\n#  define LZ4HC_HEAPMODE 1\n#endif\n\n\n/*===    Dependency    ===*/\n#define LZ4_HC_STATIC_LINKING_ONLY\n#include \"lz4hc.h\"\n\n\n/*===   Common definitions   ===*/\n#if defined(__GNUC__)\n#  pragma GCC diagnostic ignored \"-Wunused-function\"\n#endif\n#if defined (__clang__)\n#  pragma clang diagnostic ignored \"-Wunused-function\"\n#endif\n\n#define LZ4_COMMONDEFS_ONLY\n#ifndef LZ4_SRC_INCLUDED\n#include \"lz4.c\"   /* LZ4_count, constants, mem */\n#endif\n\n\n/*===   Enums   ===*/\ntypedef enum { noDictCtx, usingDictCtxHc } dictCtx_directive;\n\n\n/*===   Constants   ===*/\n#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH)\n#define LZ4_OPT_NUM   (1<<12)\n\n\n/*===   Macros   ===*/\n#define MIN(a,b)   ( (a) < (b) ? (a) : (b) )\n#define MAX(a,b)   ( (a) > (b) ? (a) : (b) )\n#define HASH_FUNCTION(i)         (((i) * 2654435761U) >> ((MINMATCH*8)-LZ4HC_HASH_LOG))\n#define DELTANEXTMAXD(p)         chainTable[(p) & LZ4HC_MAXD_MASK]    /* flexible, LZ4HC_MAXD dependent */\n#define DELTANEXTU16(table, pos) table[(U16)(pos)]   /* faster */\n/* Make fields passed to, and updated by LZ4HC_encodeSequence explicit */\n#define UPDATABLE(ip, op, anchor) &ip, &op, &anchor\n\nstatic U32 LZ4HC_hashPtr(const void* ptr) { return HASH_FUNCTION(LZ4_read32(ptr)); }\n\n\n/**************************************\n*  HC Compression\n**************************************/\nstatic void LZ4HC_clearTables (LZ4HC_CCtx_internal* hc4)\n{\n    MEM_INIT(hc4->hashTable, 0, sizeof(hc4->hashTable));\n    MEM_INIT(hc4->chainTable, 0xFF, sizeof(hc4->chainTable));\n}\n\nstatic void LZ4HC_init_internal (LZ4HC_CCtx_internal* hc4, const BYTE* start)\n{\n    size_t const bufferSize = (size_t)(hc4->end - hc4->prefixStart);\n    size_t newStartingOffset = bufferSize + hc4->dictLimit;\n    assert(newStartingOffset >= bufferSize);  /* check overflow */\n    if (newStartingOffset > 1 GB) {\n        LZ4HC_clearTables(hc4);\n        newStartingOffset = 0;\n    }\n    newStartingOffset += 64 KB;\n    hc4->nextToUpdate = (U32)newStartingOffset;\n    hc4->prefixStart = start;\n    hc4->end = start;\n    hc4->dictStart = start;\n    hc4->dictLimit = (U32)newStartingOffset;\n    hc4->lowLimit = (U32)newStartingOffset;\n}\n\n\n/* Update chains up to ip (excluded) */\nLZ4_FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip)\n{\n    U16* const chainTable = hc4->chainTable;\n    U32* const hashTable  = hc4->hashTable;\n    const BYTE* const prefixPtr = hc4->prefixStart;\n    U32 const prefixIdx = hc4->dictLimit;\n    U32 const target = (U32)(ip - prefixPtr) + prefixIdx;\n    U32 idx = hc4->nextToUpdate;\n    assert(ip >= prefixPtr);\n    assert(target >= prefixIdx);\n\n    while (idx < target) {\n        U32 const h = LZ4HC_hashPtr(prefixPtr+idx-prefixIdx);\n        size_t delta = idx - hashTable[h];\n        if (delta>LZ4_DISTANCE_MAX) delta = LZ4_DISTANCE_MAX;\n        DELTANEXTU16(chainTable, idx) = (U16)delta;\n        hashTable[h] = idx;\n        idx++;\n    }\n\n    hc4->nextToUpdate = target;\n}\n\n/** LZ4HC_countBack() :\n * @return : negative value, nb of common bytes before ip/match */\nLZ4_FORCE_INLINE\nint LZ4HC_countBack(const BYTE* const ip, const BYTE* const match,\n                    const BYTE* const iMin, const BYTE* const mMin)\n{\n    int back = 0;\n    int const min = (int)MAX(iMin - ip, mMin - match);\n    assert(min <= 0);\n    assert(ip >= iMin); assert((size_t)(ip-iMin) < (1U<<31));\n    assert(match >= mMin); assert((size_t)(match - mMin) < (1U<<31));\n    while ( (back > min)\n         && (ip[back-1] == match[back-1]) )\n            back--;\n    return back;\n}\n\n#if defined(_MSC_VER)\n#  define LZ4HC_rotl32(x,r) _rotl(x,r)\n#else\n#  define LZ4HC_rotl32(x,r) ((x << r) | (x >> (32 - r)))\n#endif\n\n\nstatic U32 LZ4HC_rotatePattern(size_t const rotate, U32 const pattern)\n{\n    size_t const bitsToRotate = (rotate & (sizeof(pattern) - 1)) << 3;\n    if (bitsToRotate == 0) return pattern;\n    return LZ4HC_rotl32(pattern, (int)bitsToRotate);\n}\n\n/* LZ4HC_countPattern() :\n * pattern32 must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) */\nstatic unsigned\nLZ4HC_countPattern(const BYTE* ip, const BYTE* const iEnd, U32 const pattern32)\n{\n    const BYTE* const iStart = ip;\n    reg_t const pattern = (sizeof(pattern)==8) ?\n        (reg_t)pattern32 + (((reg_t)pattern32) << (sizeof(pattern)*4)) : pattern32;\n\n    while (likely(ip < iEnd-(sizeof(pattern)-1))) {\n        reg_t const diff = LZ4_read_ARCH(ip) ^ pattern;\n        if (!diff) { ip+=sizeof(pattern); continue; }\n        ip += LZ4_NbCommonBytes(diff);\n        return (unsigned)(ip - iStart);\n    }\n\n    if (LZ4_isLittleEndian()) {\n        reg_t patternByte = pattern;\n        while ((ip<iEnd) && (*ip == (BYTE)patternByte)) {\n            ip++; patternByte >>= 8;\n        }\n    } else {  /* big endian */\n        U32 bitOffset = (sizeof(pattern)*8) - 8;\n        while (ip < iEnd) {\n            BYTE const byte = (BYTE)(pattern >> bitOffset);\n            if (*ip != byte) break;\n            ip ++; bitOffset -= 8;\n    }   }\n\n    return (unsigned)(ip - iStart);\n}\n\n/* LZ4HC_reverseCountPattern() :\n * pattern must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!)\n * read using natural platform endianness */\nstatic unsigned\nLZ4HC_reverseCountPattern(const BYTE* ip, const BYTE* const iLow, U32 pattern)\n{\n    const BYTE* const iStart = ip;\n\n    while (likely(ip >= iLow+4)) {\n        if (LZ4_read32(ip-4) != pattern) break;\n        ip -= 4;\n    }\n    {   const BYTE* bytePtr = (const BYTE*)(&pattern) + 3; /* works for any endianness */\n        while (likely(ip>iLow)) {\n            if (ip[-1] != *bytePtr) break;\n            ip--; bytePtr--;\n    }   }\n    return (unsigned)(iStart - ip);\n}\n\n/* LZ4HC_protectDictEnd() :\n * Checks if the match is in the last 3 bytes of the dictionary, so reading the\n * 4 byte MINMATCH would overflow.\n * @returns true if the match index is okay.\n */\nstatic int LZ4HC_protectDictEnd(U32 const dictLimit, U32 const matchIndex)\n{\n    return ((U32)((dictLimit - 1) - matchIndex) >= 3);\n}\n\ntypedef enum { rep_untested, rep_not, rep_confirmed } repeat_state_e;\ntypedef enum { favorCompressionRatio=0, favorDecompressionSpeed } HCfavor_e;\n\nLZ4_FORCE_INLINE int\nLZ4HC_InsertAndGetWiderMatch (\n        LZ4HC_CCtx_internal* const hc4,\n        const BYTE* const ip,\n        const BYTE* const iLowLimit, const BYTE* const iHighLimit,\n        int longest,\n        const BYTE** matchpos,\n        const BYTE** startpos,\n        const int maxNbAttempts,\n        const int patternAnalysis, const int chainSwap,\n        const dictCtx_directive dict,\n        const HCfavor_e favorDecSpeed)\n{\n    U16* const chainTable = hc4->chainTable;\n    U32* const HashTable = hc4->hashTable;\n    const LZ4HC_CCtx_internal * const dictCtx = hc4->dictCtx;\n    const BYTE* const prefixPtr = hc4->prefixStart;\n    const U32 prefixIdx = hc4->dictLimit;\n    const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx;\n    const int withinStartDistance = (hc4->lowLimit + (LZ4_DISTANCE_MAX + 1) > ipIndex);\n    const U32 lowestMatchIndex = (withinStartDistance) ? hc4->lowLimit : ipIndex - LZ4_DISTANCE_MAX;\n    const BYTE* const dictStart = hc4->dictStart;\n    const U32 dictIdx = hc4->lowLimit;\n    const BYTE* const dictEnd = dictStart + prefixIdx - dictIdx;\n    int const lookBackLength = (int)(ip-iLowLimit);\n    int nbAttempts = maxNbAttempts;\n    U32 matchChainPos = 0;\n    U32 const pattern = LZ4_read32(ip);\n    U32 matchIndex;\n    repeat_state_e repeat = rep_untested;\n    size_t srcPatternLength = 0;\n\n    DEBUGLOG(7, \"LZ4HC_InsertAndGetWiderMatch\");\n    /* First Match */\n    LZ4HC_Insert(hc4, ip);\n    matchIndex = HashTable[LZ4HC_hashPtr(ip)];\n    DEBUGLOG(7, \"First match at index %u / %u (lowestMatchIndex)\",\n                matchIndex, lowestMatchIndex);\n\n    while ((matchIndex>=lowestMatchIndex) && (nbAttempts>0)) {\n        int matchLength=0;\n        nbAttempts--;\n        assert(matchIndex < ipIndex);\n        if (favorDecSpeed && (ipIndex - matchIndex < 8)) {\n            /* do nothing */\n        } else if (matchIndex >= prefixIdx) {   /* within current Prefix */\n            const BYTE* const matchPtr = prefixPtr + matchIndex - prefixIdx;\n            assert(matchPtr < ip);\n            assert(longest >= 1);\n            if (LZ4_read16(iLowLimit + longest - 1) == LZ4_read16(matchPtr - lookBackLength + longest - 1)) {\n                if (LZ4_read32(matchPtr) == pattern) {\n                    int const back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, prefixPtr) : 0;\n                    matchLength = MINMATCH + (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit);\n                    matchLength -= back;\n                    if (matchLength > longest) {\n                        longest = matchLength;\n                        *matchpos = matchPtr + back;\n                        *startpos = ip + back;\n            }   }   }\n        } else {   /* lowestMatchIndex <= matchIndex < dictLimit */\n            const BYTE* const matchPtr = dictStart + (matchIndex - dictIdx);\n            assert(matchIndex >= dictIdx);\n            if ( likely(matchIndex <= prefixIdx - 4)\n              && (LZ4_read32(matchPtr) == pattern) ) {\n                int back = 0;\n                const BYTE* vLimit = ip + (prefixIdx - matchIndex);\n                if (vLimit > iHighLimit) vLimit = iHighLimit;\n                matchLength = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH;\n                if ((ip+matchLength == vLimit) && (vLimit < iHighLimit))\n                    matchLength += LZ4_count(ip+matchLength, prefixPtr, iHighLimit);\n                back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictStart) : 0;\n                matchLength -= back;\n                if (matchLength > longest) {\n                    longest = matchLength;\n                    *matchpos = prefixPtr - prefixIdx + matchIndex + back;   /* virtual pos, relative to ip, to retrieve offset */\n                    *startpos = ip + back;\n        }   }   }\n\n        if (chainSwap && matchLength==longest) {   /* better match => select a better chain */\n            assert(lookBackLength==0);   /* search forward only */\n            if (matchIndex + (U32)longest <= ipIndex) {\n                int const kTrigger = 4;\n                U32 distanceToNextMatch = 1;\n                int const end = longest - MINMATCH + 1;\n                int step = 1;\n                int accel = 1 << kTrigger;\n                int pos;\n                for (pos = 0; pos < end; pos += step) {\n                    U32 const candidateDist = DELTANEXTU16(chainTable, matchIndex + (U32)pos);\n                    step = (accel++ >> kTrigger);\n                    if (candidateDist > distanceToNextMatch) {\n                        distanceToNextMatch = candidateDist;\n                        matchChainPos = (U32)pos;\n                        accel = 1 << kTrigger;\n                }   }\n                if (distanceToNextMatch > 1) {\n                    if (distanceToNextMatch > matchIndex) break;   /* avoid overflow */\n                    matchIndex -= distanceToNextMatch;\n                    continue;\n        }   }   }\n\n        {   U32 const distNextMatch = DELTANEXTU16(chainTable, matchIndex);\n            if (patternAnalysis && distNextMatch==1 && matchChainPos==0) {\n                U32 const matchCandidateIdx = matchIndex-1;\n                /* may be a repeated pattern */\n                if (repeat == rep_untested) {\n                    if ( ((pattern & 0xFFFF) == (pattern >> 16))\n                      &  ((pattern & 0xFF)   == (pattern >> 24)) ) {\n                        repeat = rep_confirmed;\n                        srcPatternLength = LZ4HC_countPattern(ip+sizeof(pattern), iHighLimit, pattern) + sizeof(pattern);\n                    } else {\n                        repeat = rep_not;\n                }   }\n                if ( (repeat == rep_confirmed) && (matchCandidateIdx >= lowestMatchIndex)\n                  && LZ4HC_protectDictEnd(prefixIdx, matchCandidateIdx) ) {\n                    const int extDict = matchCandidateIdx < prefixIdx;\n                    const BYTE* const matchPtr = (extDict ? dictStart - dictIdx : prefixPtr - prefixIdx) + matchCandidateIdx;\n                    if (LZ4_read32(matchPtr) == pattern) {  /* good candidate */\n                        const BYTE* const iLimit = extDict ? dictEnd : iHighLimit;\n                        size_t forwardPatternLength = LZ4HC_countPattern(matchPtr+sizeof(pattern), iLimit, pattern) + sizeof(pattern);\n                        if (extDict && matchPtr + forwardPatternLength == iLimit) {\n                            U32 const rotatedPattern = LZ4HC_rotatePattern(forwardPatternLength, pattern);\n                            forwardPatternLength += LZ4HC_countPattern(prefixPtr, iHighLimit, rotatedPattern);\n                        }\n                        {   const BYTE* const lowestMatchPtr = extDict ? dictStart : prefixPtr;\n                            size_t backLength = LZ4HC_reverseCountPattern(matchPtr, lowestMatchPtr, pattern);\n                            size_t currentSegmentLength;\n                            if (!extDict\n                              && matchPtr - backLength == prefixPtr\n                              && dictIdx < prefixIdx) {\n                                U32 const rotatedPattern = LZ4HC_rotatePattern((U32)(-(int)backLength), pattern);\n                                backLength += LZ4HC_reverseCountPattern(dictEnd, dictStart, rotatedPattern);\n                            }\n                            /* Limit backLength not go further than lowestMatchIndex */\n                            backLength = matchCandidateIdx - MAX(matchCandidateIdx - (U32)backLength, lowestMatchIndex);\n                            assert(matchCandidateIdx - backLength >= lowestMatchIndex);\n                            currentSegmentLength = backLength + forwardPatternLength;\n                            /* Adjust to end of pattern if the source pattern fits, otherwise the beginning of the pattern */\n                            if ( (currentSegmentLength >= srcPatternLength)   /* current pattern segment large enough to contain full srcPatternLength */\n                              && (forwardPatternLength <= srcPatternLength) ) { /* haven't reached this position yet */\n                                U32 const newMatchIndex = matchCandidateIdx + (U32)forwardPatternLength - (U32)srcPatternLength;  /* best position, full pattern, might be followed by more match */\n                                if (LZ4HC_protectDictEnd(prefixIdx, newMatchIndex))\n                                    matchIndex = newMatchIndex;\n                                else {\n                                    /* Can only happen if started in the prefix */\n                                    assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict);\n                                    matchIndex = prefixIdx;\n                                }\n                            } else {\n                                U32 const newMatchIndex = matchCandidateIdx - (U32)backLength;   /* farthest position in current segment, will find a match of length currentSegmentLength + maybe some back */\n                                if (!LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) {\n                                    assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict);\n                                    matchIndex = prefixIdx;\n                                } else {\n                                    matchIndex = newMatchIndex;\n                                    if (lookBackLength==0) {  /* no back possible */\n                                        size_t const maxML = MIN(currentSegmentLength, srcPatternLength);\n                                        if ((size_t)longest < maxML) {\n                                            assert(prefixPtr - prefixIdx + matchIndex != ip);\n                                            if ((size_t)(ip - prefixPtr) + prefixIdx - matchIndex > LZ4_DISTANCE_MAX) break;\n                                            assert(maxML < 2 GB);\n                                            longest = (int)maxML;\n                                            *matchpos = prefixPtr - prefixIdx + matchIndex;   /* virtual pos, relative to ip, to retrieve offset */\n                                            *startpos = ip;\n                                        }\n                                        {   U32 const distToNextPattern = DELTANEXTU16(chainTable, matchIndex);\n                                            if (distToNextPattern > matchIndex) break;  /* avoid overflow */\n                                            matchIndex -= distToNextPattern;\n                        }   }   }   }   }\n                        continue;\n                }   }\n        }   }   /* PA optimization */\n\n        /* follow current chain */\n        matchIndex -= DELTANEXTU16(chainTable, matchIndex + matchChainPos);\n\n    }  /* while ((matchIndex>=lowestMatchIndex) && (nbAttempts)) */\n\n    if ( dict == usingDictCtxHc\n      && nbAttempts > 0\n      && ipIndex - lowestMatchIndex < LZ4_DISTANCE_MAX) {\n        size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit;\n        U32 dictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)];\n        assert(dictEndOffset <= 1 GB);\n        matchIndex = dictMatchIndex + lowestMatchIndex - (U32)dictEndOffset;\n        while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX && nbAttempts--) {\n            const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + dictMatchIndex;\n\n            if (LZ4_read32(matchPtr) == pattern) {\n                int mlt;\n                int back = 0;\n                const BYTE* vLimit = ip + (dictEndOffset - dictMatchIndex);\n                if (vLimit > iHighLimit) vLimit = iHighLimit;\n                mlt = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH;\n                back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->prefixStart) : 0;\n                mlt -= back;\n                if (mlt > longest) {\n                    longest = mlt;\n                    *matchpos = prefixPtr - prefixIdx + matchIndex + back;\n                    *startpos = ip + back;\n            }   }\n\n            {   U32 const nextOffset = DELTANEXTU16(dictCtx->chainTable, dictMatchIndex);\n                dictMatchIndex -= nextOffset;\n                matchIndex -= nextOffset;\n    }   }   }\n\n    return longest;\n}\n\nLZ4_FORCE_INLINE int\nLZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4,   /* Index table will be updated */\n                       const BYTE* const ip, const BYTE* const iLimit,\n                       const BYTE** matchpos,\n                       const int maxNbAttempts,\n                       const int patternAnalysis,\n                       const dictCtx_directive dict)\n{\n    const BYTE* uselessPtr = ip;\n    /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos),\n     * but this won't be the case here, as we define iLowLimit==ip,\n     * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */\n    return LZ4HC_InsertAndGetWiderMatch(hc4, ip, ip, iLimit, MINMATCH-1, matchpos, &uselessPtr, maxNbAttempts, patternAnalysis, 0 /*chainSwap*/, dict, favorCompressionRatio);\n}\n\n/* LZ4HC_encodeSequence() :\n * @return : 0 if ok,\n *           1 if buffer issue detected */\nLZ4_FORCE_INLINE int LZ4HC_encodeSequence (\n    const BYTE** _ip,\n    BYTE** _op,\n    const BYTE** _anchor,\n    int matchLength,\n    const BYTE* const match,\n    limitedOutput_directive limit,\n    BYTE* oend)\n{\n#define ip      (*_ip)\n#define op      (*_op)\n#define anchor  (*_anchor)\n\n    size_t length;\n    BYTE* const token = op++;\n\n#if defined(LZ4_DEBUG) && (LZ4_DEBUG >= 6)\n    static const BYTE* start = NULL;\n    static U32 totalCost = 0;\n    U32 const pos = (start==NULL) ? 0 : (U32)(anchor - start);\n    U32 const ll = (U32)(ip - anchor);\n    U32 const llAdd = (ll>=15) ? ((ll-15) / 255) + 1 : 0;\n    U32 const mlAdd = (matchLength>=19) ? ((matchLength-19) / 255) + 1 : 0;\n    U32 const cost = 1 + llAdd + ll + 2 + mlAdd;\n    if (start==NULL) start = anchor;  /* only works for single segment */\n    /* g_debuglog_enable = (pos >= 2228) & (pos <= 2262); */\n    DEBUGLOG(6, \"pos:%7u -- literals:%4u, match:%4i, offset:%5u, cost:%4u + %5u\",\n                pos,\n                (U32)(ip - anchor), matchLength, (U32)(ip-match),\n                cost, totalCost);\n    totalCost += cost;\n#endif\n\n    /* Encode Literal length */\n    length = (size_t)(ip - anchor);\n    LZ4_STATIC_ASSERT(notLimited == 0);\n    /* Check output limit */\n    if (limit && ((op + (length / 255) + length + (2 + 1 + LASTLITERALS)) > oend)) {\n        DEBUGLOG(6, \"Not enough room to write %i literals (%i bytes remaining)\",\n                (int)length, (int)(oend - op));\n        return 1;\n    }\n    if (length >= RUN_MASK) {\n        size_t len = length - RUN_MASK;\n        *token = (RUN_MASK << ML_BITS);\n        for(; len >= 255 ; len -= 255) *op++ = 255;\n        *op++ = (BYTE)len;\n    } else {\n        *token = (BYTE)(length << ML_BITS);\n    }\n\n    /* Copy Literals */\n    LZ4_wildCopy8(op, anchor, op + length);\n    op += length;\n\n    /* Encode Offset */\n    assert( (ip - match) <= LZ4_DISTANCE_MAX );   /* note : consider providing offset as a value, rather than as a pointer difference */\n    LZ4_writeLE16(op, (U16)(ip - match)); op += 2;\n\n    /* Encode MatchLength */\n    assert(matchLength >= MINMATCH);\n    length = (size_t)matchLength - MINMATCH;\n    if (limit && (op + (length / 255) + (1 + LASTLITERALS) > oend)) {\n        DEBUGLOG(6, \"Not enough room to write match length\");\n        return 1;   /* Check output limit */\n    }\n    if (length >= ML_MASK) {\n        *token += ML_MASK;\n        length -= ML_MASK;\n        for(; length >= 510 ; length -= 510) { *op++ = 255; *op++ = 255; }\n        if (length >= 255) { length -= 255; *op++ = 255; }\n        *op++ = (BYTE)length;\n    } else {\n        *token += (BYTE)(length);\n    }\n\n    /* Prepare next loop */\n    ip += matchLength;\n    anchor = ip;\n\n    return 0;\n}\n#undef ip\n#undef op\n#undef anchor\n\nLZ4_FORCE_INLINE int LZ4HC_compress_hashChain (\n    LZ4HC_CCtx_internal* const ctx,\n    const char* const source,\n    char* const dest,\n    int* srcSizePtr,\n    int const maxOutputSize,\n    int maxNbAttempts,\n    const limitedOutput_directive limit,\n    const dictCtx_directive dict\n    )\n{\n    const int inputSize = *srcSizePtr;\n    const int patternAnalysis = (maxNbAttempts > 128);   /* levels 9+ */\n\n    const BYTE* ip = (const BYTE*) source;\n    const BYTE* anchor = ip;\n    const BYTE* const iend = ip + inputSize;\n    const BYTE* const mflimit = iend - MFLIMIT;\n    const BYTE* const matchlimit = (iend - LASTLITERALS);\n\n    BYTE* optr = (BYTE*) dest;\n    BYTE* op = (BYTE*) dest;\n    BYTE* oend = op + maxOutputSize;\n\n    int   ml0, ml, ml2, ml3;\n    const BYTE* start0;\n    const BYTE* ref0;\n    const BYTE* ref = NULL;\n    const BYTE* start2 = NULL;\n    const BYTE* ref2 = NULL;\n    const BYTE* start3 = NULL;\n    const BYTE* ref3 = NULL;\n\n    /* init */\n    *srcSizePtr = 0;\n    if (limit == fillOutput) oend -= LASTLITERALS;                  /* Hack for support LZ4 format restriction */\n    if (inputSize < LZ4_minLength) goto _last_literals;             /* Input too small, no compression (all literals) */\n\n    /* Main Loop */\n    while (ip <= mflimit) {\n        ml = LZ4HC_InsertAndFindBestMatch(ctx, ip, matchlimit, &ref, maxNbAttempts, patternAnalysis, dict);\n        if (ml<MINMATCH) { ip++; continue; }\n\n        /* saved, in case we would skip too much */\n        start0 = ip; ref0 = ref; ml0 = ml;\n\n_Search2:\n        if (ip+ml <= mflimit) {\n            ml2 = LZ4HC_InsertAndGetWiderMatch(ctx,\n                            ip + ml - 2, ip + 0, matchlimit, ml, &ref2, &start2,\n                            maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio);\n        } else {\n            ml2 = ml;\n        }\n\n        if (ml2 == ml) { /* No better match => encode ML1 */\n            optr = op;\n            if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow;\n            continue;\n        }\n\n        if (start0 < ip) {   /* first match was skipped at least once */\n            if (start2 < ip + ml0) {  /* squeezing ML1 between ML0(original ML1) and ML2 */\n                ip = start0; ref = ref0; ml = ml0;  /* restore initial ML1 */\n        }   }\n\n        /* Here, start0==ip */\n        if ((start2 - ip) < 3) {  /* First Match too small : removed */\n            ml = ml2;\n            ip = start2;\n            ref =ref2;\n            goto _Search2;\n        }\n\n_Search3:\n        /* At this stage, we have :\n        *  ml2 > ml1, and\n        *  ip1+3 <= ip2 (usually < ip1+ml1) */\n        if ((start2 - ip) < OPTIMAL_ML) {\n            int correction;\n            int new_ml = ml;\n            if (new_ml > OPTIMAL_ML) new_ml = OPTIMAL_ML;\n            if (ip+new_ml > start2 + ml2 - MINMATCH) new_ml = (int)(start2 - ip) + ml2 - MINMATCH;\n            correction = new_ml - (int)(start2 - ip);\n            if (correction > 0) {\n                start2 += correction;\n                ref2 += correction;\n                ml2 -= correction;\n            }\n        }\n        /* Now, we have start2 = ip+new_ml, with new_ml = min(ml, OPTIMAL_ML=18) */\n\n        if (start2 + ml2 <= mflimit) {\n            ml3 = LZ4HC_InsertAndGetWiderMatch(ctx,\n                            start2 + ml2 - 3, start2, matchlimit, ml2, &ref3, &start3,\n                            maxNbAttempts, patternAnalysis, 0, dict, favorCompressionRatio);\n        } else {\n            ml3 = ml2;\n        }\n\n        if (ml3 == ml2) {  /* No better match => encode ML1 and ML2 */\n            /* ip & ref are known; Now for ml */\n            if (start2 < ip+ml)  ml = (int)(start2 - ip);\n            /* Now, encode 2 sequences */\n            optr = op;\n            if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow;\n            ip = start2;\n            optr = op;\n            if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml2, ref2, limit, oend)) {\n                ml  = ml2;\n                ref = ref2;\n                goto _dest_overflow;\n            }\n            continue;\n        }\n\n        if (start3 < ip+ml+3) {  /* Not enough space for match 2 : remove it */\n            if (start3 >= (ip+ml)) {  /* can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1 */\n                if (start2 < ip+ml) {\n                    int correction = (int)(ip+ml - start2);\n                    start2 += correction;\n                    ref2 += correction;\n                    ml2 -= correction;\n                    if (ml2 < MINMATCH) {\n                        start2 = start3;\n                        ref2 = ref3;\n                        ml2 = ml3;\n                    }\n                }\n\n                optr = op;\n                if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow;\n                ip  = start3;\n                ref = ref3;\n                ml  = ml3;\n\n                start0 = start2;\n                ref0 = ref2;\n                ml0 = ml2;\n                goto _Search2;\n            }\n\n            start2 = start3;\n            ref2 = ref3;\n            ml2 = ml3;\n            goto _Search3;\n        }\n\n        /*\n        * OK, now we have 3 ascending matches;\n        * let's write the first one ML1.\n        * ip & ref are known; Now decide ml.\n        */\n        if (start2 < ip+ml) {\n            if ((start2 - ip) < OPTIMAL_ML) {\n                int correction;\n                if (ml > OPTIMAL_ML) ml = OPTIMAL_ML;\n                if (ip + ml > start2 + ml2 - MINMATCH) ml = (int)(start2 - ip) + ml2 - MINMATCH;\n                correction = ml - (int)(start2 - ip);\n                if (correction > 0) {\n                    start2 += correction;\n                    ref2 += correction;\n                    ml2 -= correction;\n                }\n            } else {\n                ml = (int)(start2 - ip);\n            }\n        }\n        optr = op;\n        if (LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, limit, oend)) goto _dest_overflow;\n\n        /* ML2 becomes ML1 */\n        ip = start2; ref = ref2; ml = ml2;\n\n        /* ML3 becomes ML2 */\n        start2 = start3; ref2 = ref3; ml2 = ml3;\n\n        /* let's find a new ML3 */\n        goto _Search3;\n    }\n\n_last_literals:\n    /* Encode Last Literals */\n    {   size_t lastRunSize = (size_t)(iend - anchor);  /* literals */\n        size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255;\n        size_t const totalSize = 1 + llAdd + lastRunSize;\n        if (limit == fillOutput) oend += LASTLITERALS;  /* restore correct value */\n        if (limit && (op + totalSize > oend)) {\n            if (limit == limitedOutput) return 0;\n            /* adapt lastRunSize to fill 'dest' */\n            lastRunSize  = (size_t)(oend - op) - 1 /*token*/;\n            llAdd = (lastRunSize + 256 - RUN_MASK) / 256;\n            lastRunSize -= llAdd;\n        }\n        DEBUGLOG(6, \"Final literal run : %i literals\", (int)lastRunSize);\n        ip = anchor + lastRunSize;  /* can be != iend if limit==fillOutput */\n\n        if (lastRunSize >= RUN_MASK) {\n            size_t accumulator = lastRunSize - RUN_MASK;\n            *op++ = (RUN_MASK << ML_BITS);\n            for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255;\n            *op++ = (BYTE) accumulator;\n        } else {\n            *op++ = (BYTE)(lastRunSize << ML_BITS);\n        }\n        LZ4_memcpy(op, anchor, lastRunSize);\n        op += lastRunSize;\n    }\n\n    /* End */\n    *srcSizePtr = (int) (((const char*)ip) - source);\n    return (int) (((char*)op)-dest);\n\n_dest_overflow:\n    if (limit == fillOutput) {\n        /* Assumption : ip, anchor, ml and ref must be set correctly */\n        size_t const ll = (size_t)(ip - anchor);\n        size_t const ll_addbytes = (ll + 240) / 255;\n        size_t const ll_totalCost = 1 + ll_addbytes + ll;\n        BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */\n        DEBUGLOG(6, \"Last sequence overflowing\");\n        op = optr;  /* restore correct out pointer */\n        if (op + ll_totalCost <= maxLitPos) {\n            /* ll validated; now adjust match length */\n            size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost));\n            size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255);\n            assert(maxMlSize < INT_MAX); assert(ml >= 0);\n            if ((size_t)ml > maxMlSize) ml = (int)maxMlSize;\n            if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + ml >= MFLIMIT) {\n                LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ref, notLimited, oend);\n        }   }\n        goto _last_literals;\n    }\n    /* compression failed */\n    return 0;\n}\n\n\nstatic int LZ4HC_compress_optimal( LZ4HC_CCtx_internal* ctx,\n    const char* const source, char* dst,\n    int* srcSizePtr, int dstCapacity,\n    int const nbSearches, size_t sufficient_len,\n    const limitedOutput_directive limit, int const fullUpdate,\n    const dictCtx_directive dict,\n    const HCfavor_e favorDecSpeed);\n\n\nLZ4_FORCE_INLINE int LZ4HC_compress_generic_internal (\n    LZ4HC_CCtx_internal* const ctx,\n    const char* const src,\n    char* const dst,\n    int* const srcSizePtr,\n    int const dstCapacity,\n    int cLevel,\n    const limitedOutput_directive limit,\n    const dictCtx_directive dict\n    )\n{\n    typedef enum { lz4hc, lz4opt } lz4hc_strat_e;\n    typedef struct {\n        lz4hc_strat_e strat;\n        int nbSearches;\n        U32 targetLength;\n    } cParams_t;\n    static const cParams_t clTable[LZ4HC_CLEVEL_MAX+1] = {\n        { lz4hc,     2, 16 },  /* 0, unused */\n        { lz4hc,     2, 16 },  /* 1, unused */\n        { lz4hc,     2, 16 },  /* 2, unused */\n        { lz4hc,     4, 16 },  /* 3 */\n        { lz4hc,     8, 16 },  /* 4 */\n        { lz4hc,    16, 16 },  /* 5 */\n        { lz4hc,    32, 16 },  /* 6 */\n        { lz4hc,    64, 16 },  /* 7 */\n        { lz4hc,   128, 16 },  /* 8 */\n        { lz4hc,   256, 16 },  /* 9 */\n        { lz4opt,   96, 64 },  /*10==LZ4HC_CLEVEL_OPT_MIN*/\n        { lz4opt,  512,128 },  /*11 */\n        { lz4opt,16384,LZ4_OPT_NUM },  /* 12==LZ4HC_CLEVEL_MAX */\n    };\n\n    DEBUGLOG(4, \"LZ4HC_compress_generic(ctx=%p, src=%p, srcSize=%d, limit=%d)\",\n                ctx, src, *srcSizePtr, limit);\n\n    if (limit == fillOutput && dstCapacity < 1) return 0;   /* Impossible to store anything */\n    if ((U32)*srcSizePtr > (U32)LZ4_MAX_INPUT_SIZE) return 0;    /* Unsupported input size (too large or negative) */\n\n    ctx->end += *srcSizePtr;\n    if (cLevel < 1) cLevel = LZ4HC_CLEVEL_DEFAULT;   /* note : convention is different from lz4frame, maybe something to review */\n    cLevel = MIN(LZ4HC_CLEVEL_MAX, cLevel);\n    {   cParams_t const cParam = clTable[cLevel];\n        HCfavor_e const favor = ctx->favorDecSpeed ? favorDecompressionSpeed : favorCompressionRatio;\n        int result;\n\n        if (cParam.strat == lz4hc) {\n            result = LZ4HC_compress_hashChain(ctx,\n                                src, dst, srcSizePtr, dstCapacity,\n                                cParam.nbSearches, limit, dict);\n        } else {\n            assert(cParam.strat == lz4opt);\n            result = LZ4HC_compress_optimal(ctx,\n                                src, dst, srcSizePtr, dstCapacity,\n                                cParam.nbSearches, cParam.targetLength, limit,\n                                cLevel == LZ4HC_CLEVEL_MAX,   /* ultra mode */\n                                dict, favor);\n        }\n        if (result <= 0) ctx->dirty = 1;\n        return result;\n    }\n}\n\nstatic void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock);\n\nstatic int\nLZ4HC_compress_generic_noDictCtx (\n        LZ4HC_CCtx_internal* const ctx,\n        const char* const src,\n        char* const dst,\n        int* const srcSizePtr,\n        int const dstCapacity,\n        int cLevel,\n        limitedOutput_directive limit\n        )\n{\n    assert(ctx->dictCtx == NULL);\n    return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, noDictCtx);\n}\n\nstatic int\nLZ4HC_compress_generic_dictCtx (\n        LZ4HC_CCtx_internal* const ctx,\n        const char* const src,\n        char* const dst,\n        int* const srcSizePtr,\n        int const dstCapacity,\n        int cLevel,\n        limitedOutput_directive limit\n        )\n{\n    const size_t position = (size_t)(ctx->end - ctx->prefixStart) + (ctx->dictLimit - ctx->lowLimit);\n    assert(ctx->dictCtx != NULL);\n    if (position >= 64 KB) {\n        ctx->dictCtx = NULL;\n        return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit);\n    } else if (position == 0 && *srcSizePtr > 4 KB) {\n        LZ4_memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal));\n        LZ4HC_setExternalDict(ctx, (const BYTE *)src);\n        ctx->compressionLevel = (short)cLevel;\n        return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit);\n    } else {\n        return LZ4HC_compress_generic_internal(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit, usingDictCtxHc);\n    }\n}\n\nstatic int\nLZ4HC_compress_generic (\n        LZ4HC_CCtx_internal* const ctx,\n        const char* const src,\n        char* const dst,\n        int* const srcSizePtr,\n        int const dstCapacity,\n        int cLevel,\n        limitedOutput_directive limit\n        )\n{\n    if (ctx->dictCtx == NULL) {\n        return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit);\n    } else {\n        return LZ4HC_compress_generic_dictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit);\n    }\n}\n\n\nint LZ4_sizeofStateHC(void) { return (int)sizeof(LZ4_streamHC_t); }\n\nstatic size_t LZ4_streamHC_t_alignment(void)\n{\n#if LZ4_ALIGN_TEST\n    typedef struct { char c; LZ4_streamHC_t t; } t_a;\n    return sizeof(t_a) - sizeof(LZ4_streamHC_t);\n#else\n    return 1;  /* effectively disabled */\n#endif\n}\n\n/* state is presumed correctly initialized,\n * in which case its size and alignment have already been validate */\nint LZ4_compress_HC_extStateHC_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel)\n{\n    LZ4HC_CCtx_internal* const ctx = &((LZ4_streamHC_t*)state)->internal_donotuse;\n    if (!LZ4_isAligned(state, LZ4_streamHC_t_alignment())) return 0;\n    LZ4_resetStreamHC_fast((LZ4_streamHC_t*)state, compressionLevel);\n    LZ4HC_init_internal (ctx, (const BYTE*)src);\n    if (dstCapacity < LZ4_compressBound(srcSize))\n        return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, limitedOutput);\n    else\n        return LZ4HC_compress_generic (ctx, src, dst, &srcSize, dstCapacity, compressionLevel, notLimited);\n}\n\nint LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel)\n{\n    LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx));\n    if (ctx==NULL) return 0;   /* init failure */\n    return LZ4_compress_HC_extStateHC_fastReset(state, src, dst, srcSize, dstCapacity, compressionLevel);\n}\n\nint LZ4_compress_HC(const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel)\n{\n    int cSize;\n#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1\n    LZ4_streamHC_t* const statePtr = (LZ4_streamHC_t*)ALLOC(sizeof(LZ4_streamHC_t));\n    if (statePtr==NULL) return 0;\n#else\n    LZ4_streamHC_t state;\n    LZ4_streamHC_t* const statePtr = &state;\n#endif\n    cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel);\n#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1\n    FREEMEM(statePtr);\n#endif\n    return cSize;\n}\n\n/* state is presumed sized correctly (>= sizeof(LZ4_streamHC_t)) */\nint LZ4_compress_HC_destSize(void* state, const char* source, char* dest, int* sourceSizePtr, int targetDestSize, int cLevel)\n{\n    LZ4_streamHC_t* const ctx = LZ4_initStreamHC(state, sizeof(*ctx));\n    if (ctx==NULL) return 0;   /* init failure */\n    LZ4HC_init_internal(&ctx->internal_donotuse, (const BYTE*) source);\n    LZ4_setCompressionLevel(ctx, cLevel);\n    return LZ4HC_compress_generic(&ctx->internal_donotuse, source, dest, sourceSizePtr, targetDestSize, cLevel, fillOutput);\n}\n\n\n\n/**************************************\n*  Streaming Functions\n**************************************/\n/* allocation */\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4_streamHC_t* LZ4_createStreamHC(void)\n{\n    LZ4_streamHC_t* const state =\n        (LZ4_streamHC_t*)ALLOC_AND_ZERO(sizeof(LZ4_streamHC_t));\n    if (state == NULL) return NULL;\n    LZ4_setCompressionLevel(state, LZ4HC_CLEVEL_DEFAULT);\n    return state;\n}\n\nint LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr)\n{\n    DEBUGLOG(4, \"LZ4_freeStreamHC(%p)\", LZ4_streamHCPtr);\n    if (!LZ4_streamHCPtr) return 0;  /* support free on NULL */\n    FREEMEM(LZ4_streamHCPtr);\n    return 0;\n}\n#endif\n\n\nLZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size)\n{\n    LZ4_streamHC_t* const LZ4_streamHCPtr = (LZ4_streamHC_t*)buffer;\n    DEBUGLOG(4, \"LZ4_initStreamHC(%p, %u)\", buffer, (unsigned)size);\n    /* check conditions */\n    if (buffer == NULL) return NULL;\n    if (size < sizeof(LZ4_streamHC_t)) return NULL;\n    if (!LZ4_isAligned(buffer, LZ4_streamHC_t_alignment())) return NULL;\n    /* init */\n    { LZ4HC_CCtx_internal* const hcstate = &(LZ4_streamHCPtr->internal_donotuse);\n      MEM_INIT(hcstate, 0, sizeof(*hcstate)); }\n    LZ4_setCompressionLevel(LZ4_streamHCPtr, LZ4HC_CLEVEL_DEFAULT);\n    return LZ4_streamHCPtr;\n}\n\n/* just a stub */\nvoid LZ4_resetStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel)\n{\n    LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr));\n    LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel);\n}\n\nvoid LZ4_resetStreamHC_fast (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel)\n{\n    DEBUGLOG(4, \"LZ4_resetStreamHC_fast(%p, %d)\", LZ4_streamHCPtr, compressionLevel);\n    if (LZ4_streamHCPtr->internal_donotuse.dirty) {\n        LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr));\n    } else {\n        /* preserve end - prefixStart : can trigger clearTable's threshold */\n        if (LZ4_streamHCPtr->internal_donotuse.end != NULL) {\n            LZ4_streamHCPtr->internal_donotuse.end -= (uptrval)LZ4_streamHCPtr->internal_donotuse.prefixStart;\n        } else {\n            assert(LZ4_streamHCPtr->internal_donotuse.prefixStart == NULL);\n        }\n        LZ4_streamHCPtr->internal_donotuse.prefixStart = NULL;\n        LZ4_streamHCPtr->internal_donotuse.dictCtx = NULL;\n    }\n    LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel);\n}\n\nvoid LZ4_setCompressionLevel(LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel)\n{\n    DEBUGLOG(5, \"LZ4_setCompressionLevel(%p, %d)\", LZ4_streamHCPtr, compressionLevel);\n    if (compressionLevel < 1) compressionLevel = LZ4HC_CLEVEL_DEFAULT;\n    if (compressionLevel > LZ4HC_CLEVEL_MAX) compressionLevel = LZ4HC_CLEVEL_MAX;\n    LZ4_streamHCPtr->internal_donotuse.compressionLevel = (short)compressionLevel;\n}\n\nvoid LZ4_favorDecompressionSpeed(LZ4_streamHC_t* LZ4_streamHCPtr, int favor)\n{\n    LZ4_streamHCPtr->internal_donotuse.favorDecSpeed = (favor!=0);\n}\n\n/* LZ4_loadDictHC() :\n * LZ4_streamHCPtr is presumed properly initialized */\nint LZ4_loadDictHC (LZ4_streamHC_t* LZ4_streamHCPtr,\n              const char* dictionary, int dictSize)\n{\n    LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse;\n    DEBUGLOG(4, \"LZ4_loadDictHC(ctx:%p, dict:%p, dictSize:%d)\", LZ4_streamHCPtr, dictionary, dictSize);\n    assert(LZ4_streamHCPtr != NULL);\n    if (dictSize > 64 KB) {\n        dictionary += (size_t)dictSize - 64 KB;\n        dictSize = 64 KB;\n    }\n    /* need a full initialization, there are bad side-effects when using resetFast() */\n    {   int const cLevel = ctxPtr->compressionLevel;\n        LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr));\n        LZ4_setCompressionLevel(LZ4_streamHCPtr, cLevel);\n    }\n    LZ4HC_init_internal (ctxPtr, (const BYTE*)dictionary);\n    ctxPtr->end = (const BYTE*)dictionary + dictSize;\n    if (dictSize >= 4) LZ4HC_Insert (ctxPtr, ctxPtr->end-3);\n    return dictSize;\n}\n\nvoid LZ4_attach_HC_dictionary(LZ4_streamHC_t *working_stream, const LZ4_streamHC_t *dictionary_stream) {\n    working_stream->internal_donotuse.dictCtx = dictionary_stream != NULL ? &(dictionary_stream->internal_donotuse) : NULL;\n}\n\n/* compression */\n\nstatic void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock)\n{\n    DEBUGLOG(4, \"LZ4HC_setExternalDict(%p, %p)\", ctxPtr, newBlock);\n    if (ctxPtr->end >= ctxPtr->prefixStart + 4)\n        LZ4HC_Insert (ctxPtr, ctxPtr->end-3);   /* Referencing remaining dictionary content */\n\n    /* Only one memory segment for extDict, so any previous extDict is lost at this stage */\n    ctxPtr->lowLimit  = ctxPtr->dictLimit;\n    ctxPtr->dictStart  = ctxPtr->prefixStart;\n    ctxPtr->dictLimit += (U32)(ctxPtr->end - ctxPtr->prefixStart);\n    ctxPtr->prefixStart = newBlock;\n    ctxPtr->end  = newBlock;\n    ctxPtr->nextToUpdate = ctxPtr->dictLimit;   /* match referencing will resume from there */\n\n    /* cannot reference an extDict and a dictCtx at the same time */\n    ctxPtr->dictCtx = NULL;\n}\n\nstatic int\nLZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr,\n                                 const char* src, char* dst,\n                                 int* srcSizePtr, int dstCapacity,\n                                 limitedOutput_directive limit)\n{\n    LZ4HC_CCtx_internal* const ctxPtr = &LZ4_streamHCPtr->internal_donotuse;\n    DEBUGLOG(5, \"LZ4_compressHC_continue_generic(ctx=%p, src=%p, srcSize=%d, limit=%d)\",\n                LZ4_streamHCPtr, src, *srcSizePtr, limit);\n    assert(ctxPtr != NULL);\n    /* auto-init if forgotten */\n    if (ctxPtr->prefixStart == NULL) LZ4HC_init_internal (ctxPtr, (const BYTE*) src);\n\n    /* Check overflow */\n    if ((size_t)(ctxPtr->end - ctxPtr->prefixStart) + ctxPtr->dictLimit > 2 GB) {\n        size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->prefixStart);\n        if (dictSize > 64 KB) dictSize = 64 KB;\n        LZ4_loadDictHC(LZ4_streamHCPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize);\n    }\n\n    /* Check if blocks follow each other */\n    if ((const BYTE*)src != ctxPtr->end)\n        LZ4HC_setExternalDict(ctxPtr, (const BYTE*)src);\n\n    /* Check overlapping input/dictionary space */\n    {   const BYTE* sourceEnd = (const BYTE*) src + *srcSizePtr;\n        const BYTE* const dictBegin = ctxPtr->dictStart;\n        const BYTE* const dictEnd   = ctxPtr->dictStart + (ctxPtr->dictLimit - ctxPtr->lowLimit);\n        if ((sourceEnd > dictBegin) && ((const BYTE*)src < dictEnd)) {\n            if (sourceEnd > dictEnd) sourceEnd = dictEnd;\n            ctxPtr->lowLimit += (U32)(sourceEnd - ctxPtr->dictStart);\n            ctxPtr->dictStart += (U32)(sourceEnd - ctxPtr->dictStart);\n            if (ctxPtr->dictLimit - ctxPtr->lowLimit < 4) {\n                ctxPtr->lowLimit = ctxPtr->dictLimit;\n                ctxPtr->dictStart = ctxPtr->prefixStart;\n    }   }   }\n\n    return LZ4HC_compress_generic (ctxPtr, src, dst, srcSizePtr, dstCapacity, ctxPtr->compressionLevel, limit);\n}\n\nint LZ4_compress_HC_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int srcSize, int dstCapacity)\n{\n    if (dstCapacity < LZ4_compressBound(srcSize))\n        return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, limitedOutput);\n    else\n        return LZ4_compressHC_continue_generic (LZ4_streamHCPtr, src, dst, &srcSize, dstCapacity, notLimited);\n}\n\nint LZ4_compress_HC_continue_destSize (LZ4_streamHC_t* LZ4_streamHCPtr, const char* src, char* dst, int* srcSizePtr, int targetDestSize)\n{\n    return LZ4_compressHC_continue_generic(LZ4_streamHCPtr, src, dst, srcSizePtr, targetDestSize, fillOutput);\n}\n\n\n\n/* LZ4_saveDictHC :\n * save history content\n * into a user-provided buffer\n * which is then used to continue compression\n */\nint LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize)\n{\n    LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse;\n    int const prefixSize = (int)(streamPtr->end - streamPtr->prefixStart);\n    DEBUGLOG(5, \"LZ4_saveDictHC(%p, %p, %d)\", LZ4_streamHCPtr, safeBuffer, dictSize);\n    assert(prefixSize >= 0);\n    if (dictSize > 64 KB) dictSize = 64 KB;\n    if (dictSize < 4) dictSize = 0;\n    if (dictSize > prefixSize) dictSize = prefixSize;\n    if (safeBuffer == NULL) assert(dictSize == 0);\n    if (dictSize > 0)\n        LZ4_memmove(safeBuffer, streamPtr->end - dictSize, dictSize);\n    {   U32 const endIndex = (U32)(streamPtr->end - streamPtr->prefixStart) + streamPtr->dictLimit;\n        streamPtr->end = (const BYTE*)safeBuffer + dictSize;\n        streamPtr->prefixStart = streamPtr->end - dictSize;\n        streamPtr->dictLimit = endIndex - (U32)dictSize;\n        streamPtr->lowLimit = endIndex - (U32)dictSize;\n        streamPtr->dictStart = streamPtr->prefixStart;\n        if (streamPtr->nextToUpdate < streamPtr->dictLimit)\n            streamPtr->nextToUpdate = streamPtr->dictLimit;\n    }\n    return dictSize;\n}\n\n\n/***************************************************\n*  Deprecated Functions\n***************************************************/\n\n/* These functions currently generate deprecation warnings */\n\n/* Wrappers for deprecated compression functions */\nint LZ4_compressHC(const char* src, char* dst, int srcSize) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), 0); }\nint LZ4_compressHC_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, 0); }\nint LZ4_compressHC2(const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC (src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); }\nint LZ4_compressHC2_limitedOutput(const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC(src, dst, srcSize, maxDstSize, cLevel); }\nint LZ4_compressHC_withStateHC (void* state, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, LZ4_compressBound(srcSize), 0); }\nint LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_extStateHC (state, src, dst, srcSize, maxDstSize, 0); }\nint LZ4_compressHC2_withStateHC (void* state, const char* src, char* dst, int srcSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, LZ4_compressBound(srcSize), cLevel); }\nint LZ4_compressHC2_limitedOutput_withStateHC (void* state, const char* src, char* dst, int srcSize, int maxDstSize, int cLevel) { return LZ4_compress_HC_extStateHC(state, src, dst, srcSize, maxDstSize, cLevel); }\nint LZ4_compressHC_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, LZ4_compressBound(srcSize)); }\nint LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* ctx, const char* src, char* dst, int srcSize, int maxDstSize) { return LZ4_compress_HC_continue (ctx, src, dst, srcSize, maxDstSize); }\n\n\n/* Deprecated streaming functions */\nint LZ4_sizeofStreamStateHC(void) { return sizeof(LZ4_streamHC_t); }\n\n/* state is presumed correctly sized, aka >= sizeof(LZ4_streamHC_t)\n * @return : 0 on success, !=0 if error */\nint LZ4_resetStreamStateHC(void* state, char* inputBuffer)\n{\n    LZ4_streamHC_t* const hc4 = LZ4_initStreamHC(state, sizeof(*hc4));\n    if (hc4 == NULL) return 1;   /* init failed */\n    LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer);\n    return 0;\n}\n\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nvoid* LZ4_createHC (const char* inputBuffer)\n{\n    LZ4_streamHC_t* const hc4 = LZ4_createStreamHC();\n    if (hc4 == NULL) return NULL;   /* not enough memory */\n    LZ4HC_init_internal (&hc4->internal_donotuse, (const BYTE*)inputBuffer);\n    return hc4;\n}\n\nint LZ4_freeHC (void* LZ4HC_Data)\n{\n    if (!LZ4HC_Data) return 0;  /* support free on NULL */\n    FREEMEM(LZ4HC_Data);\n    return 0;\n}\n#endif\n\nint LZ4_compressHC2_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int cLevel)\n{\n    return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, 0, cLevel, notLimited);\n}\n\nint LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int dstCapacity, int cLevel)\n{\n    return LZ4HC_compress_generic (&((LZ4_streamHC_t*)LZ4HC_Data)->internal_donotuse, src, dst, &srcSize, dstCapacity, cLevel, limitedOutput);\n}\n\nchar* LZ4_slideInputBufferHC(void* LZ4HC_Data)\n{\n    LZ4_streamHC_t* const ctx = (LZ4_streamHC_t*)LZ4HC_Data;\n    const BYTE* bufferStart = ctx->internal_donotuse.prefixStart - ctx->internal_donotuse.dictLimit + ctx->internal_donotuse.lowLimit;\n    LZ4_resetStreamHC_fast(ctx, ctx->internal_donotuse.compressionLevel);\n    /* avoid const char * -> char * conversion warning :( */\n    return (char*)(uptrval)bufferStart;\n}\n\n\n/* ================================================\n *  LZ4 Optimal parser (levels [LZ4HC_CLEVEL_OPT_MIN - LZ4HC_CLEVEL_MAX])\n * ===============================================*/\ntypedef struct {\n    int price;\n    int off;\n    int mlen;\n    int litlen;\n} LZ4HC_optimal_t;\n\n/* price in bytes */\nLZ4_FORCE_INLINE int LZ4HC_literalsPrice(int const litlen)\n{\n    int price = litlen;\n    assert(litlen >= 0);\n    if (litlen >= (int)RUN_MASK)\n        price += 1 + ((litlen-(int)RUN_MASK) / 255);\n    return price;\n}\n\n\n/* requires mlen >= MINMATCH */\nLZ4_FORCE_INLINE int LZ4HC_sequencePrice(int litlen, int mlen)\n{\n    int price = 1 + 2 ; /* token + 16-bit offset */\n    assert(litlen >= 0);\n    assert(mlen >= MINMATCH);\n\n    price += LZ4HC_literalsPrice(litlen);\n\n    if (mlen >= (int)(ML_MASK+MINMATCH))\n        price += 1 + ((mlen-(int)(ML_MASK+MINMATCH)) / 255);\n\n    return price;\n}\n\n\ntypedef struct {\n    int off;\n    int len;\n} LZ4HC_match_t;\n\nLZ4_FORCE_INLINE LZ4HC_match_t\nLZ4HC_FindLongerMatch(LZ4HC_CCtx_internal* const ctx,\n                      const BYTE* ip, const BYTE* const iHighLimit,\n                      int minLen, int nbSearches,\n                      const dictCtx_directive dict,\n                      const HCfavor_e favorDecSpeed)\n{\n    LZ4HC_match_t match = { 0 , 0 };\n    const BYTE* matchPtr = NULL;\n    /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos),\n     * but this won't be the case here, as we define iLowLimit==ip,\n     * so LZ4HC_InsertAndGetWiderMatch() won't be allowed to search past ip */\n    int matchLength = LZ4HC_InsertAndGetWiderMatch(ctx, ip, ip, iHighLimit, minLen, &matchPtr, &ip, nbSearches, 1 /*patternAnalysis*/, 1 /*chainSwap*/, dict, favorDecSpeed);\n    if (matchLength <= minLen) return match;\n    if (favorDecSpeed) {\n        if ((matchLength>18) & (matchLength<=36)) matchLength=18;   /* favor shortcut */\n    }\n    match.len = matchLength;\n    match.off = (int)(ip-matchPtr);\n    return match;\n}\n\n\nstatic int LZ4HC_compress_optimal ( LZ4HC_CCtx_internal* ctx,\n                                    const char* const source,\n                                    char* dst,\n                                    int* srcSizePtr,\n                                    int dstCapacity,\n                                    int const nbSearches,\n                                    size_t sufficient_len,\n                                    const limitedOutput_directive limit,\n                                    int const fullUpdate,\n                                    const dictCtx_directive dict,\n                                    const HCfavor_e favorDecSpeed)\n{\n    int retval = 0;\n#define TRAILING_LITERALS 3\n#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1\n    LZ4HC_optimal_t* const opt = (LZ4HC_optimal_t*)ALLOC(sizeof(LZ4HC_optimal_t) * (LZ4_OPT_NUM + TRAILING_LITERALS));\n#else\n    LZ4HC_optimal_t opt[LZ4_OPT_NUM + TRAILING_LITERALS];   /* ~64 KB, which is a bit large for stack... */\n#endif\n\n    const BYTE* ip = (const BYTE*) source;\n    const BYTE* anchor = ip;\n    const BYTE* const iend = ip + *srcSizePtr;\n    const BYTE* const mflimit = iend - MFLIMIT;\n    const BYTE* const matchlimit = iend - LASTLITERALS;\n    BYTE* op = (BYTE*) dst;\n    BYTE* opSaved = (BYTE*) dst;\n    BYTE* oend = op + dstCapacity;\n    int ovml = MINMATCH;  /* overflow - last sequence */\n    const BYTE* ovref = NULL;\n\n    /* init */\n#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1\n    if (opt == NULL) goto _return_label;\n#endif\n    DEBUGLOG(5, \"LZ4HC_compress_optimal(dst=%p, dstCapa=%u)\", dst, (unsigned)dstCapacity);\n    *srcSizePtr = 0;\n    if (limit == fillOutput) oend -= LASTLITERALS;   /* Hack for support LZ4 format restriction */\n    if (sufficient_len >= LZ4_OPT_NUM) sufficient_len = LZ4_OPT_NUM-1;\n\n    /* Main Loop */\n    while (ip <= mflimit) {\n         int const llen = (int)(ip - anchor);\n         int best_mlen, best_off;\n         int cur, last_match_pos = 0;\n\n         LZ4HC_match_t const firstMatch = LZ4HC_FindLongerMatch(ctx, ip, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed);\n         if (firstMatch.len==0) { ip++; continue; }\n\n         if ((size_t)firstMatch.len > sufficient_len) {\n             /* good enough solution : immediate encoding */\n             int const firstML = firstMatch.len;\n             const BYTE* const matchPos = ip - firstMatch.off;\n             opSaved = op;\n             if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), firstML, matchPos, limit, oend) ) {  /* updates ip, op and anchor */\n                 ovml = firstML;\n                 ovref = matchPos;\n                 goto _dest_overflow;\n             }\n             continue;\n         }\n\n         /* set prices for first positions (literals) */\n         {   int rPos;\n             for (rPos = 0 ; rPos < MINMATCH ; rPos++) {\n                 int const cost = LZ4HC_literalsPrice(llen + rPos);\n                 opt[rPos].mlen = 1;\n                 opt[rPos].off = 0;\n                 opt[rPos].litlen = llen + rPos;\n                 opt[rPos].price = cost;\n                 DEBUGLOG(7, \"rPos:%3i => price:%3i (litlen=%i) -- initial setup\",\n                             rPos, cost, opt[rPos].litlen);\n         }   }\n         /* set prices using initial match */\n         {   int mlen = MINMATCH;\n             int const matchML = firstMatch.len;   /* necessarily < sufficient_len < LZ4_OPT_NUM */\n             int const offset = firstMatch.off;\n             assert(matchML < LZ4_OPT_NUM);\n             for ( ; mlen <= matchML ; mlen++) {\n                 int const cost = LZ4HC_sequencePrice(llen, mlen);\n                 opt[mlen].mlen = mlen;\n                 opt[mlen].off = offset;\n                 opt[mlen].litlen = llen;\n                 opt[mlen].price = cost;\n                 DEBUGLOG(7, \"rPos:%3i => price:%3i (matchlen=%i) -- initial setup\",\n                             mlen, cost, mlen);\n         }   }\n         last_match_pos = firstMatch.len;\n         {   int addLit;\n             for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) {\n                 opt[last_match_pos+addLit].mlen = 1; /* literal */\n                 opt[last_match_pos+addLit].off = 0;\n                 opt[last_match_pos+addLit].litlen = addLit;\n                 opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit);\n                 DEBUGLOG(7, \"rPos:%3i => price:%3i (litlen=%i) -- initial setup\",\n                             last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit);\n         }   }\n\n         /* check further positions */\n         for (cur = 1; cur < last_match_pos; cur++) {\n             const BYTE* const curPtr = ip + cur;\n             LZ4HC_match_t newMatch;\n\n             if (curPtr > mflimit) break;\n             DEBUGLOG(7, \"rPos:%u[%u] vs [%u]%u\",\n                     cur, opt[cur].price, opt[cur+1].price, cur+1);\n             if (fullUpdate) {\n                 /* not useful to search here if next position has same (or lower) cost */\n                 if ( (opt[cur+1].price <= opt[cur].price)\n                   /* in some cases, next position has same cost, but cost rises sharply after, so a small match would still be beneficial */\n                   && (opt[cur+MINMATCH].price < opt[cur].price + 3/*min seq price*/) )\n                     continue;\n             } else {\n                 /* not useful to search here if next position has same (or lower) cost */\n                 if (opt[cur+1].price <= opt[cur].price) continue;\n             }\n\n             DEBUGLOG(7, \"search at rPos:%u\", cur);\n             if (fullUpdate)\n                 newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, MINMATCH-1, nbSearches, dict, favorDecSpeed);\n             else\n                 /* only test matches of minimum length; slightly faster, but misses a few bytes */\n                 newMatch = LZ4HC_FindLongerMatch(ctx, curPtr, matchlimit, last_match_pos - cur, nbSearches, dict, favorDecSpeed);\n             if (!newMatch.len) continue;\n\n             if ( ((size_t)newMatch.len > sufficient_len)\n               || (newMatch.len + cur >= LZ4_OPT_NUM) ) {\n                 /* immediate encoding */\n                 best_mlen = newMatch.len;\n                 best_off = newMatch.off;\n                 last_match_pos = cur + 1;\n                 goto encode;\n             }\n\n             /* before match : set price with literals at beginning */\n             {   int const baseLitlen = opt[cur].litlen;\n                 int litlen;\n                 for (litlen = 1; litlen < MINMATCH; litlen++) {\n                     int const price = opt[cur].price - LZ4HC_literalsPrice(baseLitlen) + LZ4HC_literalsPrice(baseLitlen+litlen);\n                     int const pos = cur + litlen;\n                     if (price < opt[pos].price) {\n                         opt[pos].mlen = 1; /* literal */\n                         opt[pos].off = 0;\n                         opt[pos].litlen = baseLitlen+litlen;\n                         opt[pos].price = price;\n                         DEBUGLOG(7, \"rPos:%3i => price:%3i (litlen=%i)\",\n                                     pos, price, opt[pos].litlen);\n             }   }   }\n\n             /* set prices using match at position = cur */\n             {   int const matchML = newMatch.len;\n                 int ml = MINMATCH;\n\n                 assert(cur + newMatch.len < LZ4_OPT_NUM);\n                 for ( ; ml <= matchML ; ml++) {\n                     int const pos = cur + ml;\n                     int const offset = newMatch.off;\n                     int price;\n                     int ll;\n                     DEBUGLOG(7, \"testing price rPos %i (last_match_pos=%i)\",\n                                 pos, last_match_pos);\n                     if (opt[cur].mlen == 1) {\n                         ll = opt[cur].litlen;\n                         price = ((cur > ll) ? opt[cur - ll].price : 0)\n                               + LZ4HC_sequencePrice(ll, ml);\n                     } else {\n                         ll = 0;\n                         price = opt[cur].price + LZ4HC_sequencePrice(0, ml);\n                     }\n\n                    assert((U32)favorDecSpeed <= 1);\n                     if (pos > last_match_pos+TRAILING_LITERALS\n                      || price <= opt[pos].price - (int)favorDecSpeed) {\n                         DEBUGLOG(7, \"rPos:%3i => price:%3i (matchlen=%i)\",\n                                     pos, price, ml);\n                         assert(pos < LZ4_OPT_NUM);\n                         if ( (ml == matchML)  /* last pos of last match */\n                           && (last_match_pos < pos) )\n                             last_match_pos = pos;\n                         opt[pos].mlen = ml;\n                         opt[pos].off = offset;\n                         opt[pos].litlen = ll;\n                         opt[pos].price = price;\n             }   }   }\n             /* complete following positions with literals */\n             {   int addLit;\n                 for (addLit = 1; addLit <= TRAILING_LITERALS; addLit ++) {\n                     opt[last_match_pos+addLit].mlen = 1; /* literal */\n                     opt[last_match_pos+addLit].off = 0;\n                     opt[last_match_pos+addLit].litlen = addLit;\n                     opt[last_match_pos+addLit].price = opt[last_match_pos].price + LZ4HC_literalsPrice(addLit);\n                     DEBUGLOG(7, \"rPos:%3i => price:%3i (litlen=%i)\", last_match_pos+addLit, opt[last_match_pos+addLit].price, addLit);\n             }   }\n         }  /* for (cur = 1; cur <= last_match_pos; cur++) */\n\n         assert(last_match_pos < LZ4_OPT_NUM + TRAILING_LITERALS);\n         best_mlen = opt[last_match_pos].mlen;\n         best_off = opt[last_match_pos].off;\n         cur = last_match_pos - best_mlen;\n\nencode: /* cur, last_match_pos, best_mlen, best_off must be set */\n         assert(cur < LZ4_OPT_NUM);\n         assert(last_match_pos >= 1);  /* == 1 when only one candidate */\n         DEBUGLOG(6, \"reverse traversal, looking for shortest path (last_match_pos=%i)\", last_match_pos);\n         {   int candidate_pos = cur;\n             int selected_matchLength = best_mlen;\n             int selected_offset = best_off;\n             while (1) {  /* from end to beginning */\n                 int const next_matchLength = opt[candidate_pos].mlen;  /* can be 1, means literal */\n                 int const next_offset = opt[candidate_pos].off;\n                 DEBUGLOG(7, \"pos %i: sequence length %i\", candidate_pos, selected_matchLength);\n                 opt[candidate_pos].mlen = selected_matchLength;\n                 opt[candidate_pos].off = selected_offset;\n                 selected_matchLength = next_matchLength;\n                 selected_offset = next_offset;\n                 if (next_matchLength > candidate_pos) break; /* last match elected, first match to encode */\n                 assert(next_matchLength > 0);  /* can be 1, means literal */\n                 candidate_pos -= next_matchLength;\n         }   }\n\n         /* encode all recorded sequences in order */\n         {   int rPos = 0;  /* relative position (to ip) */\n             while (rPos < last_match_pos) {\n                 int const ml = opt[rPos].mlen;\n                 int const offset = opt[rPos].off;\n                 if (ml == 1) { ip++; rPos++; continue; }  /* literal; note: can end up with several literals, in which case, skip them */\n                 rPos += ml;\n                 assert(ml >= MINMATCH);\n                 assert((offset >= 1) && (offset <= LZ4_DISTANCE_MAX));\n                 opSaved = op;\n                 if ( LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ml, ip - offset, limit, oend) ) {  /* updates ip, op and anchor */\n                     ovml = ml;\n                     ovref = ip - offset;\n                     goto _dest_overflow;\n         }   }   }\n     }  /* while (ip <= mflimit) */\n\n_last_literals:\n     /* Encode Last Literals */\n     {   size_t lastRunSize = (size_t)(iend - anchor);  /* literals */\n         size_t llAdd = (lastRunSize + 255 - RUN_MASK) / 255;\n         size_t const totalSize = 1 + llAdd + lastRunSize;\n         if (limit == fillOutput) oend += LASTLITERALS;  /* restore correct value */\n         if (limit && (op + totalSize > oend)) {\n             if (limit == limitedOutput) { /* Check output limit */\n                retval = 0;\n                goto _return_label;\n             }\n             /* adapt lastRunSize to fill 'dst' */\n             lastRunSize  = (size_t)(oend - op) - 1 /*token*/;\n             llAdd = (lastRunSize + 256 - RUN_MASK) / 256;\n             lastRunSize -= llAdd;\n         }\n         DEBUGLOG(6, \"Final literal run : %i literals\", (int)lastRunSize);\n         ip = anchor + lastRunSize; /* can be != iend if limit==fillOutput */\n\n         if (lastRunSize >= RUN_MASK) {\n             size_t accumulator = lastRunSize - RUN_MASK;\n             *op++ = (RUN_MASK << ML_BITS);\n             for(; accumulator >= 255 ; accumulator -= 255) *op++ = 255;\n             *op++ = (BYTE) accumulator;\n         } else {\n             *op++ = (BYTE)(lastRunSize << ML_BITS);\n         }\n         LZ4_memcpy(op, anchor, lastRunSize);\n         op += lastRunSize;\n     }\n\n     /* End */\n     *srcSizePtr = (int) (((const char*)ip) - source);\n     retval = (int) ((char*)op-dst);\n     goto _return_label;\n\n_dest_overflow:\nif (limit == fillOutput) {\n     /* Assumption : ip, anchor, ovml and ovref must be set correctly */\n     size_t const ll = (size_t)(ip - anchor);\n     size_t const ll_addbytes = (ll + 240) / 255;\n     size_t const ll_totalCost = 1 + ll_addbytes + ll;\n     BYTE* const maxLitPos = oend - 3; /* 2 for offset, 1 for token */\n     DEBUGLOG(6, \"Last sequence overflowing (only %i bytes remaining)\", (int)(oend-1-opSaved));\n     op = opSaved;  /* restore correct out pointer */\n     if (op + ll_totalCost <= maxLitPos) {\n         /* ll validated; now adjust match length */\n         size_t const bytesLeftForMl = (size_t)(maxLitPos - (op+ll_totalCost));\n         size_t const maxMlSize = MINMATCH + (ML_MASK-1) + (bytesLeftForMl * 255);\n         assert(maxMlSize < INT_MAX); assert(ovml >= 0);\n         if ((size_t)ovml > maxMlSize) ovml = (int)maxMlSize;\n         if ((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1 + ovml >= MFLIMIT) {\n             DEBUGLOG(6, \"Space to end : %i + ml (%i)\", (int)((oend + LASTLITERALS) - (op + ll_totalCost + 2) - 1), ovml);\n             DEBUGLOG(6, \"Before : ip = %p, anchor = %p\", ip, anchor);\n             LZ4HC_encodeSequence(UPDATABLE(ip, op, anchor), ovml, ovref, notLimited, oend);\n             DEBUGLOG(6, \"After : ip = %p, anchor = %p\", ip, anchor);\n     }   }\n     goto _last_literals;\n}\n_return_label:\n#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1\n     FREEMEM(opt);\n#endif\n     return retval;\n}\n"
  },
  {
    "path": "third-party/l4z/lz4hc.h",
    "content": "/*\n   LZ4 HC - High Compression Mode of LZ4\n   Header File\n   Copyright (C) 2011-2020, Yann Collet.\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\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\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   You can contact the author at :\n   - LZ4 source repository : https://github.com/lz4/lz4\n   - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c\n*/\n#ifndef LZ4_HC_H_19834876238432\n#define LZ4_HC_H_19834876238432\n\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\n/* --- Dependency --- */\n/* note : lz4hc requires lz4.h/lz4.c for compilation */\n#include \"lz4.h\"   /* stddef, LZ4LIB_API, LZ4_DEPRECATED */\n\n\n/* --- Useful constants --- */\n#define LZ4HC_CLEVEL_MIN         3\n#define LZ4HC_CLEVEL_DEFAULT     9\n#define LZ4HC_CLEVEL_OPT_MIN    10\n#define LZ4HC_CLEVEL_MAX        12\n\n\n/*-************************************\n *  Block Compression\n **************************************/\n/*! LZ4_compress_HC() :\n *  Compress data from `src` into `dst`, using the powerful but slower \"HC\" algorithm.\n * `dst` must be already allocated.\n *  Compression is guaranteed to succeed if `dstCapacity >= LZ4_compressBound(srcSize)` (see \"lz4.h\")\n *  Max supported `srcSize` value is LZ4_MAX_INPUT_SIZE (see \"lz4.h\")\n * `compressionLevel` : any value between 1 and LZ4HC_CLEVEL_MAX will work.\n *                      Values > LZ4HC_CLEVEL_MAX behave the same as LZ4HC_CLEVEL_MAX.\n * @return : the number of bytes written into 'dst'\n *           or 0 if compression fails.\n */\nLZ4LIB_API int LZ4_compress_HC (const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel);\n\n\n/* Note :\n *   Decompression functions are provided within \"lz4.h\" (BSD license)\n */\n\n\n/*! LZ4_compress_HC_extStateHC() :\n *  Same as LZ4_compress_HC(), but using an externally allocated memory segment for `state`.\n * `state` size is provided by LZ4_sizeofStateHC().\n *  Memory segment must be aligned on 8-bytes boundaries (which a normal malloc() should do properly).\n */\nLZ4LIB_API int LZ4_sizeofStateHC(void);\nLZ4LIB_API int LZ4_compress_HC_extStateHC(void* stateHC, const char* src, char* dst, int srcSize, int maxDstSize, int compressionLevel);\n\n\n/*! LZ4_compress_HC_destSize() : v1.9.0+\n *  Will compress as much data as possible from `src`\n *  to fit into `targetDstSize` budget.\n *  Result is provided in 2 parts :\n * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize)\n *           or 0 if compression fails.\n * `srcSizePtr` : on success, *srcSizePtr is updated to indicate how much bytes were read from `src`\n */\nLZ4LIB_API int LZ4_compress_HC_destSize(void* stateHC,\n                                  const char* src, char* dst,\n                                        int* srcSizePtr, int targetDstSize,\n                                        int compressionLevel);\n\n\n/*-************************************\n *  Streaming Compression\n *  Bufferless synchronous API\n **************************************/\n typedef union LZ4_streamHC_u LZ4_streamHC_t;   /* incomplete type (defined later) */\n\n/*! LZ4_createStreamHC() and LZ4_freeStreamHC() :\n *  These functions create and release memory for LZ4 HC streaming state.\n *  Newly created states are automatically initialized.\n *  A same state can be used multiple times consecutively,\n *  starting with LZ4_resetStreamHC_fast() to start a new stream of blocks.\n */\nLZ4LIB_API LZ4_streamHC_t* LZ4_createStreamHC(void);\nLZ4LIB_API int             LZ4_freeStreamHC (LZ4_streamHC_t* streamHCPtr);\n\n/*\n  These functions compress data in successive blocks of any size,\n  using previous blocks as dictionary, to improve compression ratio.\n  One key assumption is that previous blocks (up to 64 KB) remain read-accessible while compressing next blocks.\n  There is an exception for ring buffers, which can be smaller than 64 KB.\n  Ring-buffer scenario is automatically detected and handled within LZ4_compress_HC_continue().\n\n  Before starting compression, state must be allocated and properly initialized.\n  LZ4_createStreamHC() does both, though compression level is set to LZ4HC_CLEVEL_DEFAULT.\n\n  Selecting the compression level can be done with LZ4_resetStreamHC_fast() (starts a new stream)\n  or LZ4_setCompressionLevel() (anytime, between blocks in the same stream) (experimental).\n  LZ4_resetStreamHC_fast() only works on states which have been properly initialized at least once,\n  which is automatically the case when state is created using LZ4_createStreamHC().\n\n  After reset, a first \"fictional block\" can be designated as initial dictionary,\n  using LZ4_loadDictHC() (Optional).\n\n  Invoke LZ4_compress_HC_continue() to compress each successive block.\n  The number of blocks is unlimited.\n  Previous input blocks, including initial dictionary when present,\n  must remain accessible and unmodified during compression.\n\n  It's allowed to update compression level anytime between blocks,\n  using LZ4_setCompressionLevel() (experimental).\n\n  'dst' buffer should be sized to handle worst case scenarios\n  (see LZ4_compressBound(), it ensures compression success).\n  In case of failure, the API does not guarantee recovery,\n  so the state _must_ be reset.\n  To ensure compression success\n  whenever `dst` buffer size cannot be made >= LZ4_compressBound(),\n  consider using LZ4_compress_HC_continue_destSize().\n\n  Whenever previous input blocks can't be preserved unmodified in-place during compression of next blocks,\n  it's possible to copy the last blocks into a more stable memory space, using LZ4_saveDictHC().\n  Return value of LZ4_saveDictHC() is the size of dictionary effectively saved into 'safeBuffer' (<= 64 KB)\n\n  After completing a streaming compression,\n  it's possible to start a new stream of blocks, using the same LZ4_streamHC_t state,\n  just by resetting it, using LZ4_resetStreamHC_fast().\n*/\n\nLZ4LIB_API void LZ4_resetStreamHC_fast(LZ4_streamHC_t* streamHCPtr, int compressionLevel);   /* v1.9.0+ */\nLZ4LIB_API int  LZ4_loadDictHC (LZ4_streamHC_t* streamHCPtr, const char* dictionary, int dictSize);\n\nLZ4LIB_API int LZ4_compress_HC_continue (LZ4_streamHC_t* streamHCPtr,\n                                   const char* src, char* dst,\n                                         int srcSize, int maxDstSize);\n\n/*! LZ4_compress_HC_continue_destSize() : v1.9.0+\n *  Similar to LZ4_compress_HC_continue(),\n *  but will read as much data as possible from `src`\n *  to fit into `targetDstSize` budget.\n *  Result is provided into 2 parts :\n * @return : the number of bytes written into 'dst' (necessarily <= targetDstSize)\n *           or 0 if compression fails.\n * `srcSizePtr` : on success, *srcSizePtr will be updated to indicate how much bytes were read from `src`.\n *           Note that this function may not consume the entire input.\n */\nLZ4LIB_API int LZ4_compress_HC_continue_destSize(LZ4_streamHC_t* LZ4_streamHCPtr,\n                                           const char* src, char* dst,\n                                                 int* srcSizePtr, int targetDstSize);\n\nLZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, int maxDictSize);\n\n\n\n/*^**********************************************\n * !!!!!!   STATIC LINKING ONLY   !!!!!!\n ***********************************************/\n\n/*-******************************************************************\n * PRIVATE DEFINITIONS :\n * Do not use these definitions directly.\n * They are merely exposed to allow static allocation of `LZ4_streamHC_t`.\n * Declare an `LZ4_streamHC_t` directly, rather than any type below.\n * Even then, only do so in the context of static linking, as definitions may change between versions.\n ********************************************************************/\n\n#define LZ4HC_DICTIONARY_LOGSIZE 16\n#define LZ4HC_MAXD (1<<LZ4HC_DICTIONARY_LOGSIZE)\n#define LZ4HC_MAXD_MASK (LZ4HC_MAXD - 1)\n\n#define LZ4HC_HASH_LOG 15\n#define LZ4HC_HASHTABLESIZE (1 << LZ4HC_HASH_LOG)\n#define LZ4HC_HASH_MASK (LZ4HC_HASHTABLESIZE - 1)\n\n\n/* Never ever use these definitions directly !\n * Declare or allocate an LZ4_streamHC_t instead.\n**/\ntypedef struct LZ4HC_CCtx_internal LZ4HC_CCtx_internal;\nstruct LZ4HC_CCtx_internal\n{\n    LZ4_u32   hashTable[LZ4HC_HASHTABLESIZE];\n    LZ4_u16   chainTable[LZ4HC_MAXD];\n    const LZ4_byte* end;       /* next block here to continue on current prefix */\n    const LZ4_byte* prefixStart;  /* Indexes relative to this position */\n    const LZ4_byte* dictStart; /* alternate reference for extDict */\n    LZ4_u32   dictLimit;       /* below that point, need extDict */\n    LZ4_u32   lowLimit;        /* below that point, no more dict */\n    LZ4_u32   nextToUpdate;    /* index from which to continue dictionary update */\n    short     compressionLevel;\n    LZ4_i8    favorDecSpeed;   /* favor decompression speed if this flag set,\n                                  otherwise, favor compression ratio */\n    LZ4_i8    dirty;           /* stream has to be fully reset if this flag is set */\n    const LZ4HC_CCtx_internal* dictCtx;\n};\n\n#define LZ4_STREAMHC_MINSIZE  262200  /* static size, for inter-version compatibility */\nunion LZ4_streamHC_u {\n    char minStateSize[LZ4_STREAMHC_MINSIZE];\n    LZ4HC_CCtx_internal internal_donotuse;\n}; /* previously typedef'd to LZ4_streamHC_t */\n\n/* LZ4_streamHC_t :\n * This structure allows static allocation of LZ4 HC streaming state.\n * This can be used to allocate statically on stack, or as part of a larger structure.\n *\n * Such state **must** be initialized using LZ4_initStreamHC() before first use.\n *\n * Note that invoking LZ4_initStreamHC() is not required when\n * the state was created using LZ4_createStreamHC() (which is recommended).\n * Using the normal builder, a newly created state is automatically initialized.\n *\n * Static allocation shall only be used in combination with static linking.\n */\n\n/* LZ4_initStreamHC() : v1.9.0+\n * Required before first use of a statically allocated LZ4_streamHC_t.\n * Before v1.9.0 : use LZ4_resetStreamHC() instead\n */\nLZ4LIB_API LZ4_streamHC_t* LZ4_initStreamHC(void* buffer, size_t size);\n\n\n/*-************************************\n*  Deprecated Functions\n**************************************/\n/* see lz4.h LZ4_DISABLE_DEPRECATE_WARNINGS to turn off deprecation warnings */\n\n/* deprecated compression functions */\nLZ4_DEPRECATED(\"use LZ4_compress_HC() instead\") LZ4LIB_API int LZ4_compressHC               (const char* source, char* dest, int inputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_HC() instead\") LZ4LIB_API int LZ4_compressHC_limitedOutput (const char* source, char* dest, int inputSize, int maxOutputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_HC() instead\") LZ4LIB_API int LZ4_compressHC2              (const char* source, char* dest, int inputSize, int compressionLevel);\nLZ4_DEPRECATED(\"use LZ4_compress_HC() instead\") LZ4LIB_API int LZ4_compressHC2_limitedOutput(const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);\nLZ4_DEPRECATED(\"use LZ4_compress_HC_extStateHC() instead\") LZ4LIB_API int LZ4_compressHC_withStateHC               (void* state, const char* source, char* dest, int inputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_HC_extStateHC() instead\") LZ4LIB_API int LZ4_compressHC_limitedOutput_withStateHC (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_HC_extStateHC() instead\") LZ4LIB_API int LZ4_compressHC2_withStateHC              (void* state, const char* source, char* dest, int inputSize, int compressionLevel);\nLZ4_DEPRECATED(\"use LZ4_compress_HC_extStateHC() instead\") LZ4LIB_API int LZ4_compressHC2_limitedOutput_withStateHC(void* state, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);\nLZ4_DEPRECATED(\"use LZ4_compress_HC_continue() instead\") LZ4LIB_API int LZ4_compressHC_continue               (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize);\nLZ4_DEPRECATED(\"use LZ4_compress_HC_continue() instead\") LZ4LIB_API int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* LZ4_streamHCPtr, const char* source, char* dest, int inputSize, int maxOutputSize);\n\n/* Obsolete streaming functions; degraded functionality; do not use!\n *\n * In order to perform streaming compression, these functions depended on data\n * that is no longer tracked in the state. They have been preserved as well as\n * possible: using them will still produce a correct output. However, use of\n * LZ4_slideInputBufferHC() will truncate the history of the stream, rather\n * than preserve a window-sized chunk of history.\n */\n#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)\nLZ4_DEPRECATED(\"use LZ4_createStreamHC() instead\") LZ4LIB_API void* LZ4_createHC (const char* inputBuffer);\nLZ4_DEPRECATED(\"use LZ4_freeStreamHC() instead\") LZ4LIB_API   int   LZ4_freeHC (void* LZ4HC_Data);\n#endif\nLZ4_DEPRECATED(\"use LZ4_saveDictHC() instead\") LZ4LIB_API     char* LZ4_slideInputBufferHC (void* LZ4HC_Data);\nLZ4_DEPRECATED(\"use LZ4_compress_HC_continue() instead\") LZ4LIB_API int LZ4_compressHC2_continue               (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel);\nLZ4_DEPRECATED(\"use LZ4_compress_HC_continue() instead\") LZ4LIB_API int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel);\nLZ4_DEPRECATED(\"use LZ4_createStreamHC() instead\") LZ4LIB_API int   LZ4_sizeofStreamStateHC(void);\nLZ4_DEPRECATED(\"use LZ4_initStreamHC() instead\") LZ4LIB_API  int   LZ4_resetStreamStateHC(void* state, char* inputBuffer);\n\n\n/* LZ4_resetStreamHC() is now replaced by LZ4_initStreamHC().\n * The intention is to emphasize the difference with LZ4_resetStreamHC_fast(),\n * which is now the recommended function to start a new stream of blocks,\n * but cannot be used to initialize a memory segment containing arbitrary garbage data.\n *\n * It is recommended to switch to LZ4_initStreamHC().\n * LZ4_resetStreamHC() will generate deprecation warnings in a future version.\n */\nLZ4LIB_API void LZ4_resetStreamHC (LZ4_streamHC_t* streamHCPtr, int compressionLevel);\n\n\n#if defined (__cplusplus)\n}\n#endif\n\n#endif /* LZ4_HC_H_19834876238432 */\n\n\n/*-**************************************************\n * !!!!!     STATIC LINKING ONLY     !!!!!\n * Following definitions are considered experimental.\n * They should not be linked from DLL,\n * as there is no guarantee of API stability yet.\n * Prototypes will be promoted to \"stable\" status\n * after successful usage in real-life scenarios.\n ***************************************************/\n#ifdef LZ4_HC_STATIC_LINKING_ONLY   /* protection macro */\n#ifndef LZ4_HC_SLO_098092834\n#define LZ4_HC_SLO_098092834\n\n#define LZ4_STATIC_LINKING_ONLY   /* LZ4LIB_STATIC_API */\n#include \"lz4.h\"\n\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\n/*! LZ4_setCompressionLevel() : v1.8.0+ (experimental)\n *  It's possible to change compression level\n *  between successive invocations of LZ4_compress_HC_continue*()\n *  for dynamic adaptation.\n */\nLZ4LIB_STATIC_API void LZ4_setCompressionLevel(\n    LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel);\n\n/*! LZ4_favorDecompressionSpeed() : v1.8.2+ (experimental)\n *  Opt. Parser will favor decompression speed over compression ratio.\n *  Only applicable to levels >= LZ4HC_CLEVEL_OPT_MIN.\n */\nLZ4LIB_STATIC_API void LZ4_favorDecompressionSpeed(\n    LZ4_streamHC_t* LZ4_streamHCPtr, int favor);\n\n/*! LZ4_resetStreamHC_fast() : v1.9.0+\n *  When an LZ4_streamHC_t is known to be in a internally coherent state,\n *  it can often be prepared for a new compression with almost no work, only\n *  sometimes falling back to the full, expensive reset that is always required\n *  when the stream is in an indeterminate state (i.e., the reset performed by\n *  LZ4_resetStreamHC()).\n *\n *  LZ4_streamHCs are guaranteed to be in a valid state when:\n *  - returned from LZ4_createStreamHC()\n *  - reset by LZ4_resetStreamHC()\n *  - memset(stream, 0, sizeof(LZ4_streamHC_t))\n *  - the stream was in a valid state and was reset by LZ4_resetStreamHC_fast()\n *  - the stream was in a valid state and was then used in any compression call\n *    that returned success\n *  - the stream was in an indeterminate state and was used in a compression\n *    call that fully reset the state (LZ4_compress_HC_extStateHC()) and that\n *    returned success\n *\n *  Note:\n *  A stream that was last used in a compression call that returned an error\n *  may be passed to this function. However, it will be fully reset, which will\n *  clear any existing history and settings from the context.\n */\nLZ4LIB_STATIC_API void LZ4_resetStreamHC_fast(\n    LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLevel);\n\n/*! LZ4_compress_HC_extStateHC_fastReset() :\n *  A variant of LZ4_compress_HC_extStateHC().\n *\n *  Using this variant avoids an expensive initialization step. It is only safe\n *  to call if the state buffer is known to be correctly initialized already\n *  (see above comment on LZ4_resetStreamHC_fast() for a definition of\n *  \"correctly initialized\"). From a high level, the difference is that this\n *  function initializes the provided state with a call to\n *  LZ4_resetStreamHC_fast() while LZ4_compress_HC_extStateHC() starts with a\n *  call to LZ4_resetStreamHC().\n */\nLZ4LIB_STATIC_API int LZ4_compress_HC_extStateHC_fastReset (\n    void* state,\n    const char* src, char* dst,\n    int srcSize, int dstCapacity,\n    int compressionLevel);\n\n/*! LZ4_attach_HC_dictionary() :\n *  This is an experimental API that allows for the efficient use of a\n *  static dictionary many times.\n *\n *  Rather than re-loading the dictionary buffer into a working context before\n *  each compression, or copying a pre-loaded dictionary's LZ4_streamHC_t into a\n *  working LZ4_streamHC_t, this function introduces a no-copy setup mechanism,\n *  in which the working stream references the dictionary stream in-place.\n *\n *  Several assumptions are made about the state of the dictionary stream.\n *  Currently, only streams which have been prepared by LZ4_loadDictHC() should\n *  be expected to work.\n *\n *  Alternatively, the provided dictionary stream pointer may be NULL, in which\n *  case any existing dictionary stream is unset.\n *\n *  A dictionary should only be attached to a stream without any history (i.e.,\n *  a stream that has just been reset).\n *\n *  The dictionary will remain attached to the working stream only for the\n *  current stream session. Calls to LZ4_resetStreamHC(_fast) will remove the\n *  dictionary context association from the working stream. The dictionary\n *  stream (and source buffer) must remain in-place / accessible / unchanged\n *  through the lifetime of the stream session.\n */\nLZ4LIB_STATIC_API void LZ4_attach_HC_dictionary(\n          LZ4_streamHC_t *working_stream,\n    const LZ4_streamHC_t *dictionary_stream);\n\n#if defined (__cplusplus)\n}\n#endif\n\n#endif   /* LZ4_HC_SLO_098092834 */\n#endif   /* LZ4_HC_STATIC_LINKING_ONLY */\n"
  },
  {
    "path": "third-party/l4z/xxhash.c",
    "content": "/*\n*  xxHash - Fast Hash algorithm\n*  Copyright (C) 2012-2016, Yann Collet\n*\n*  BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\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*\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*  You can contact the author at :\n*  - xxHash homepage: http://www.xxhash.com\n*  - xxHash source repository : https://github.com/Cyan4973/xxHash\n*/\n\n\n/* *************************************\n*  Tuning parameters\n***************************************/\n/*!XXH_FORCE_MEMORY_ACCESS :\n * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.\n * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.\n * The below switch allow to select different access method for improved performance.\n * Method 0 (default) : use `memcpy()`. Safe and portable.\n * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).\n *            This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.\n * Method 2 : direct access. This method doesn't depend on compiler but violate C standard.\n *            It can generate buggy code on targets which do not support unaligned memory accesses.\n *            But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)\n * See http://stackoverflow.com/a/32095106/646947 for details.\n * Prefer these methods in priority order (0 > 1 > 2)\n */\n#ifndef XXH_FORCE_MEMORY_ACCESS   /* can be defined externally, on command line for example */\n#  if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \\\n                        || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \\\n                        || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) )\n#    define XXH_FORCE_MEMORY_ACCESS 2\n#  elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \\\n  (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \\\n                    || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \\\n                    || defined(__ARM_ARCH_7S__) ))\n#    define XXH_FORCE_MEMORY_ACCESS 1\n#  endif\n#endif\n\n/*!XXH_ACCEPT_NULL_INPUT_POINTER :\n * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault.\n * When this macro is enabled, xxHash actively checks input for null pointer.\n * It it is, result for null input pointers is the same as a null-length input.\n */\n#ifndef XXH_ACCEPT_NULL_INPUT_POINTER   /* can be defined externally */\n#  define XXH_ACCEPT_NULL_INPUT_POINTER 0\n#endif\n\n/*!XXH_FORCE_NATIVE_FORMAT :\n * By default, xxHash library provides endian-independent Hash values, based on little-endian convention.\n * Results are therefore identical for little-endian and big-endian CPU.\n * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.\n * Should endian-independence be of no importance for your application, you may set the #define below to 1,\n * to improve speed for Big-endian CPU.\n * This option has no impact on Little_Endian CPU.\n */\n#ifndef XXH_FORCE_NATIVE_FORMAT   /* can be defined externally */\n#  define XXH_FORCE_NATIVE_FORMAT 0\n#endif\n\n/*!XXH_FORCE_ALIGN_CHECK :\n * This is a minor performance trick, only useful with lots of very small keys.\n * It means : check for aligned/unaligned input.\n * The check costs one initial branch per hash;\n * set it to 0 when the input is guaranteed to be aligned,\n * or when alignment doesn't matter for performance.\n */\n#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */\n#  if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)\n#    define XXH_FORCE_ALIGN_CHECK 0\n#  else\n#    define XXH_FORCE_ALIGN_CHECK 1\n#  endif\n#endif\n\n\n/* *************************************\n*  Includes & Memory related functions\n***************************************/\n/*! Modify the local functions below should you wish to use some other memory routines\n*   for malloc(), free() */\n#include <stdlib.h>\nstatic void* XXH_malloc(size_t s) { return malloc(s); }\nstatic void  XXH_free  (void* p)  { free(p); }\n/*! and for memcpy() */\n#include <string.h>\nstatic void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }\n\n#include <assert.h>   /* assert */\n\n#define XXH_STATIC_LINKING_ONLY\n#include \"xxhash.h\"\n\n\n/* *************************************\n*  Compiler Specific Options\n***************************************/\n#ifdef _MSC_VER    /* Visual Studio */\n#  pragma warning(disable : 4127)      /* disable: C4127: conditional expression is constant */\n#  define FORCE_INLINE static __forceinline\n#else\n#  if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */\n#    ifdef __GNUC__\n#      define FORCE_INLINE static inline __attribute__((always_inline))\n#    else\n#      define FORCE_INLINE static inline\n#    endif\n#  else\n#    define FORCE_INLINE static\n#  endif /* __STDC_VERSION__ */\n#endif\n\n\n/* *************************************\n*  Basic Types\n***************************************/\n#ifndef MEM_MODULE\n# if !defined (__VMS) \\\n  && (defined (__cplusplus) \\\n  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )\n#   include <stdint.h>\n    typedef uint8_t  BYTE;\n    typedef uint16_t U16;\n    typedef uint32_t U32;\n# else\n    typedef unsigned char      BYTE;\n    typedef unsigned short     U16;\n    typedef unsigned int       U32;\n# endif\n#endif\n\n#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))\n\n/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */\nstatic U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; }\n\n#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))\n\n/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */\n/* currently only defined for gcc and icc */\ntypedef union { U32 u32; } __attribute__((packed)) unalign;\nstatic U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }\n\n#else\n\n/* portable and safe solution. Generally efficient.\n * see : http://stackoverflow.com/a/32095106/646947\n */\nstatic U32 XXH_read32(const void* memPtr)\n{\n    U32 val;\n    memcpy(&val, memPtr, sizeof(val));\n    return val;\n}\n\n#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */\n\n\n/* ****************************************\n*  Compiler-specific Functions and Macros\n******************************************/\n#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)\n\n/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */\n#if defined(_MSC_VER)\n#  define XXH_rotl32(x,r) _rotl(x,r)\n#  define XXH_rotl64(x,r) _rotl64(x,r)\n#else\n#  define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))\n#  define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r)))\n#endif\n\n#if defined(_MSC_VER)     /* Visual Studio */\n#  define XXH_swap32 _byteswap_ulong\n#elif XXH_GCC_VERSION >= 403\n#  define XXH_swap32 __builtin_bswap32\n#else\nstatic U32 XXH_swap32 (U32 x)\n{\n    return  ((x << 24) & 0xff000000 ) |\n            ((x <<  8) & 0x00ff0000 ) |\n            ((x >>  8) & 0x0000ff00 ) |\n            ((x >> 24) & 0x000000ff );\n}\n#endif\n\n\n/* *************************************\n*  Architecture Macros\n***************************************/\ntypedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;\n\n/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */\n#ifndef XXH_CPU_LITTLE_ENDIAN\nstatic int XXH_isLittleEndian(void)\n{\n    const union { U32 u; BYTE c[4]; } one = { 1 };   /* don't use static : performance detrimental  */\n    return one.c[0];\n}\n#   define XXH_CPU_LITTLE_ENDIAN   XXH_isLittleEndian()\n#endif\n\n\n/* ***************************\n*  Memory reads\n*****************************/\ntypedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;\n\nFORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align)\n{\n    if (align==XXH_unaligned)\n        return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));\n    else\n        return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);\n}\n\nFORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian)\n{\n    return XXH_readLE32_align(ptr, endian, XXH_unaligned);\n}\n\nstatic U32 XXH_readBE32(const void* ptr)\n{\n    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);\n}\n\n\n/* *************************************\n*  Macros\n***************************************/\n#define XXH_STATIC_ASSERT(c)  { enum { XXH_sa = 1/(int)(!!(c)) }; }  /* use after variable declarations */\nXXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }\n\n\n/* *******************************************************************\n*  32-bit hash functions\n*********************************************************************/\nstatic const U32 PRIME32_1 = 2654435761U;\nstatic const U32 PRIME32_2 = 2246822519U;\nstatic const U32 PRIME32_3 = 3266489917U;\nstatic const U32 PRIME32_4 =  668265263U;\nstatic const U32 PRIME32_5 =  374761393U;\n\nstatic U32 XXH32_round(U32 seed, U32 input)\n{\n    seed += input * PRIME32_2;\n    seed  = XXH_rotl32(seed, 13);\n    seed *= PRIME32_1;\n    return seed;\n}\n\n/* mix all bits */\nstatic U32 XXH32_avalanche(U32 h32)\n{\n    h32 ^= h32 >> 15;\n    h32 *= PRIME32_2;\n    h32 ^= h32 >> 13;\n    h32 *= PRIME32_3;\n    h32 ^= h32 >> 16;\n    return(h32);\n}\n\n#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align)\n\nstatic U32\nXXH32_finalize(U32 h32, const void* ptr, size_t len,\n                XXH_endianess endian, XXH_alignment align)\n\n{\n    const BYTE* p = (const BYTE*)ptr;\n\n#define PROCESS1               \\\n    h32 += (*p++) * PRIME32_5; \\\n    h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;\n\n#define PROCESS4                         \\\n    h32 += XXH_get32bits(p) * PRIME32_3; \\\n    p+=4;                                \\\n    h32  = XXH_rotl32(h32, 17) * PRIME32_4 ;\n\n    switch(len&15)  /* or switch(bEnd - p) */\n    {\n      case 12:      PROCESS4;\n                    /* fallthrough */\n      case 8:       PROCESS4;\n                    /* fallthrough */\n      case 4:       PROCESS4;\n                    return XXH32_avalanche(h32);\n\n      case 13:      PROCESS4;\n                    /* fallthrough */\n      case 9:       PROCESS4;\n                    /* fallthrough */\n      case 5:       PROCESS4;\n                    PROCESS1;\n                    return XXH32_avalanche(h32);\n\n      case 14:      PROCESS4;\n                    /* fallthrough */\n      case 10:      PROCESS4;\n                    /* fallthrough */\n      case 6:       PROCESS4;\n                    PROCESS1;\n                    PROCESS1;\n                    return XXH32_avalanche(h32);\n\n      case 15:      PROCESS4;\n                    /* fallthrough */\n      case 11:      PROCESS4;\n                    /* fallthrough */\n      case 7:       PROCESS4;\n                    /* fallthrough */\n      case 3:       PROCESS1;\n                    /* fallthrough */\n      case 2:       PROCESS1;\n                    /* fallthrough */\n      case 1:       PROCESS1;\n                    /* fallthrough */\n      case 0:       return XXH32_avalanche(h32);\n    }\n    assert(0);\n    return h32;   /* reaching this point is deemed impossible */\n}\n\n\nFORCE_INLINE U32\nXXH32_endian_align(const void* input, size_t len, U32 seed,\n                    XXH_endianess endian, XXH_alignment align)\n{\n    const BYTE* p = (const BYTE*)input;\n    const BYTE* bEnd = p + len;\n    U32 h32;\n\n#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)\n    if (p==NULL) {\n        len=0;\n        bEnd=p=(const BYTE*)(size_t)16;\n    }\n#endif\n\n    if (len>=16) {\n        const BYTE* const limit = bEnd - 15;\n        U32 v1 = seed + PRIME32_1 + PRIME32_2;\n        U32 v2 = seed + PRIME32_2;\n        U32 v3 = seed + 0;\n        U32 v4 = seed - PRIME32_1;\n\n        do {\n            v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4;\n            v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4;\n            v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4;\n            v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4;\n        } while (p < limit);\n\n        h32 = XXH_rotl32(v1, 1)  + XXH_rotl32(v2, 7)\n            + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);\n    } else {\n        h32  = seed + PRIME32_5;\n    }\n\n    h32 += (U32)len;\n\n    return XXH32_finalize(h32, p, len&15, endian, align);\n}\n\n\nXXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed)\n{\n#if 0\n    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */\n    XXH32_state_t state;\n    XXH32_reset(&state, seed);\n    XXH32_update(&state, input, len);\n    return XXH32_digest(&state);\n#else\n    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;\n\n    if (XXH_FORCE_ALIGN_CHECK) {\n        if ((((size_t)input) & 3) == 0) {   /* Input is 4-bytes aligned, leverage the speed benefit */\n            if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\n                return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);\n            else\n                return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);\n    }   }\n\n    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\n        return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);\n    else\n        return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);\n#endif\n}\n\n\n\n/*======   Hash streaming   ======*/\n\nXXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)\n{\n    return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));\n}\nXXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)\n{\n    XXH_free(statePtr);\n    return XXH_OK;\n}\n\nXXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)\n{\n    memcpy(dstState, srcState, sizeof(*dstState));\n}\n\nXXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed)\n{\n    XXH32_state_t state;   /* using a local state to memcpy() in order to avoid strict-aliasing warnings */\n    memset(&state, 0, sizeof(state));\n    state.v1 = seed + PRIME32_1 + PRIME32_2;\n    state.v2 = seed + PRIME32_2;\n    state.v3 = seed + 0;\n    state.v4 = seed - PRIME32_1;\n    /* do not write into reserved, planned to be removed in a future version */\n    memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved));\n    return XXH_OK;\n}\n\n\nFORCE_INLINE XXH_errorcode\nXXH32_update_endian(XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian)\n{\n    if (input==NULL)\n#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)\n        return XXH_OK;\n#else\n        return XXH_ERROR;\n#endif\n\n    {   const BYTE* p = (const BYTE*)input;\n        const BYTE* const bEnd = p + len;\n\n        state->total_len_32 += (unsigned)len;\n        state->large_len |= (len>=16) | (state->total_len_32>=16);\n\n        if (state->memsize + len < 16)  {   /* fill in tmp buffer */\n            XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len);\n            state->memsize += (unsigned)len;\n            return XXH_OK;\n        }\n\n        if (state->memsize) {   /* some data left from previous update */\n            XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize);\n            {   const U32* p32 = state->mem32;\n                state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++;\n                state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++;\n                state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++;\n                state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian));\n            }\n            p += 16-state->memsize;\n            state->memsize = 0;\n        }\n\n        if (p <= bEnd-16) {\n            const BYTE* const limit = bEnd - 16;\n            U32 v1 = state->v1;\n            U32 v2 = state->v2;\n            U32 v3 = state->v3;\n            U32 v4 = state->v4;\n\n            do {\n                v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4;\n                v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4;\n                v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4;\n                v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4;\n            } while (p<=limit);\n\n            state->v1 = v1;\n            state->v2 = v2;\n            state->v3 = v3;\n            state->v4 = v4;\n        }\n\n        if (p < bEnd) {\n            XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));\n            state->memsize = (unsigned)(bEnd-p);\n        }\n    }\n\n    return XXH_OK;\n}\n\n\nXXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len)\n{\n    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;\n\n    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\n        return XXH32_update_endian(state_in, input, len, XXH_littleEndian);\n    else\n        return XXH32_update_endian(state_in, input, len, XXH_bigEndian);\n}\n\n\nFORCE_INLINE U32\nXXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian)\n{\n    U32 h32;\n\n    if (state->large_len) {\n        h32 = XXH_rotl32(state->v1, 1)\n            + XXH_rotl32(state->v2, 7)\n            + XXH_rotl32(state->v3, 12)\n            + XXH_rotl32(state->v4, 18);\n    } else {\n        h32 = state->v3 /* == seed */ + PRIME32_5;\n    }\n\n    h32 += state->total_len_32;\n\n    return XXH32_finalize(h32, state->mem32, state->memsize, endian, XXH_aligned);\n}\n\n\nXXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in)\n{\n    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;\n\n    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\n        return XXH32_digest_endian(state_in, XXH_littleEndian);\n    else\n        return XXH32_digest_endian(state_in, XXH_bigEndian);\n}\n\n\n/*======   Canonical representation   ======*/\n\n/*! Default XXH result types are basic unsigned 32 and 64 bits.\n*   The canonical representation follows human-readable write convention, aka big-endian (large digits first).\n*   These functions allow transformation of hash result into and from its canonical format.\n*   This way, hash values can be written into a file or buffer, remaining comparable across different systems.\n*/\n\nXXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)\n{\n    XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));\n    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);\n    memcpy(dst, &hash, sizeof(*dst));\n}\n\nXXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)\n{\n    return XXH_readBE32(src);\n}\n\n\n#ifndef XXH_NO_LONG_LONG\n\n/* *******************************************************************\n*  64-bit hash functions\n*********************************************************************/\n\n/*======   Memory access   ======*/\n\n#ifndef MEM_MODULE\n# define MEM_MODULE\n# if !defined (__VMS) \\\n  && (defined (__cplusplus) \\\n  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )\n#   include <stdint.h>\n    typedef uint64_t U64;\n# else\n    /* if compiler doesn't support unsigned long long, replace by another 64-bit type */\n    typedef unsigned long long U64;\n# endif\n#endif\n\n\n#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))\n\n/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */\nstatic U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; }\n\n#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))\n\n/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */\n/* currently only defined for gcc and icc */\ntypedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64;\nstatic U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; }\n\n#else\n\n/* portable and safe solution. Generally efficient.\n * see : http://stackoverflow.com/a/32095106/646947\n */\n\nstatic U64 XXH_read64(const void* memPtr)\n{\n    U64 val;\n    memcpy(&val, memPtr, sizeof(val));\n    return val;\n}\n\n#endif   /* XXH_FORCE_DIRECT_MEMORY_ACCESS */\n\n#if defined(_MSC_VER)     /* Visual Studio */\n#  define XXH_swap64 _byteswap_uint64\n#elif XXH_GCC_VERSION >= 403\n#  define XXH_swap64 __builtin_bswap64\n#else\nstatic U64 XXH_swap64 (U64 x)\n{\n    return  ((x << 56) & 0xff00000000000000ULL) |\n            ((x << 40) & 0x00ff000000000000ULL) |\n            ((x << 24) & 0x0000ff0000000000ULL) |\n            ((x << 8)  & 0x000000ff00000000ULL) |\n            ((x >> 8)  & 0x00000000ff000000ULL) |\n            ((x >> 24) & 0x0000000000ff0000ULL) |\n            ((x >> 40) & 0x000000000000ff00ULL) |\n            ((x >> 56) & 0x00000000000000ffULL);\n}\n#endif\n\nFORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align)\n{\n    if (align==XXH_unaligned)\n        return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));\n    else\n        return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);\n}\n\nFORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian)\n{\n    return XXH_readLE64_align(ptr, endian, XXH_unaligned);\n}\n\nstatic U64 XXH_readBE64(const void* ptr)\n{\n    return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);\n}\n\n\n/*======   xxh64   ======*/\n\nstatic const U64 PRIME64_1 = 11400714785074694791ULL;\nstatic const U64 PRIME64_2 = 14029467366897019727ULL;\nstatic const U64 PRIME64_3 =  1609587929392839161ULL;\nstatic const U64 PRIME64_4 =  9650029242287828579ULL;\nstatic const U64 PRIME64_5 =  2870177450012600261ULL;\n\nstatic U64 XXH64_round(U64 acc, U64 input)\n{\n    acc += input * PRIME64_2;\n    acc  = XXH_rotl64(acc, 31);\n    acc *= PRIME64_1;\n    return acc;\n}\n\nstatic U64 XXH64_mergeRound(U64 acc, U64 val)\n{\n    val  = XXH64_round(0, val);\n    acc ^= val;\n    acc  = acc * PRIME64_1 + PRIME64_4;\n    return acc;\n}\n\nstatic U64 XXH64_avalanche(U64 h64)\n{\n    h64 ^= h64 >> 33;\n    h64 *= PRIME64_2;\n    h64 ^= h64 >> 29;\n    h64 *= PRIME64_3;\n    h64 ^= h64 >> 32;\n    return h64;\n}\n\n\n#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align)\n\nstatic U64\nXXH64_finalize(U64 h64, const void* ptr, size_t len,\n               XXH_endianess endian, XXH_alignment align)\n{\n    const BYTE* p = (const BYTE*)ptr;\n\n#define PROCESS1_64            \\\n    h64 ^= (*p++) * PRIME64_5; \\\n    h64 = XXH_rotl64(h64, 11) * PRIME64_1;\n\n#define PROCESS4_64          \\\n    h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; \\\n    p+=4;                    \\\n    h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;\n\n#define PROCESS8_64 {        \\\n    U64 const k1 = XXH64_round(0, XXH_get64bits(p)); \\\n    p+=8;                    \\\n    h64 ^= k1;               \\\n    h64  = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; \\\n}\n\n    switch(len&31) {\n      case 24: PROCESS8_64;\n                    /* fallthrough */\n      case 16: PROCESS8_64;\n                    /* fallthrough */\n      case  8: PROCESS8_64;\n               return XXH64_avalanche(h64);\n\n      case 28: PROCESS8_64;\n                    /* fallthrough */\n      case 20: PROCESS8_64;\n                    /* fallthrough */\n      case 12: PROCESS8_64;\n                    /* fallthrough */\n      case  4: PROCESS4_64;\n               return XXH64_avalanche(h64);\n\n      case 25: PROCESS8_64;\n                    /* fallthrough */\n      case 17: PROCESS8_64;\n                    /* fallthrough */\n      case  9: PROCESS8_64;\n               PROCESS1_64;\n               return XXH64_avalanche(h64);\n\n      case 29: PROCESS8_64;\n                    /* fallthrough */\n      case 21: PROCESS8_64;\n                    /* fallthrough */\n      case 13: PROCESS8_64;\n                    /* fallthrough */\n      case  5: PROCESS4_64;\n               PROCESS1_64;\n               return XXH64_avalanche(h64);\n\n      case 26: PROCESS8_64;\n                    /* fallthrough */\n      case 18: PROCESS8_64;\n                    /* fallthrough */\n      case 10: PROCESS8_64;\n               PROCESS1_64;\n               PROCESS1_64;\n               return XXH64_avalanche(h64);\n\n      case 30: PROCESS8_64;\n                    /* fallthrough */\n      case 22: PROCESS8_64;\n                    /* fallthrough */\n      case 14: PROCESS8_64;\n                    /* fallthrough */\n      case  6: PROCESS4_64;\n               PROCESS1_64;\n               PROCESS1_64;\n               return XXH64_avalanche(h64);\n\n      case 27: PROCESS8_64;\n                    /* fallthrough */\n      case 19: PROCESS8_64;\n                    /* fallthrough */\n      case 11: PROCESS8_64;\n               PROCESS1_64;\n               PROCESS1_64;\n               PROCESS1_64;\n               return XXH64_avalanche(h64);\n\n      case 31: PROCESS8_64;\n                    /* fallthrough */\n      case 23: PROCESS8_64;\n                    /* fallthrough */\n      case 15: PROCESS8_64;\n                    /* fallthrough */\n      case  7: PROCESS4_64;\n                    /* fallthrough */\n      case  3: PROCESS1_64;\n                    /* fallthrough */\n      case  2: PROCESS1_64;\n                    /* fallthrough */\n      case  1: PROCESS1_64;\n                    /* fallthrough */\n      case  0: return XXH64_avalanche(h64);\n    }\n\n    /* impossible to reach */\n    assert(0);\n    return 0;  /* unreachable, but some compilers complain without it */\n}\n\nFORCE_INLINE U64\nXXH64_endian_align(const void* input, size_t len, U64 seed,\n                XXH_endianess endian, XXH_alignment align)\n{\n    const BYTE* p = (const BYTE*)input;\n    const BYTE* bEnd = p + len;\n    U64 h64;\n\n#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)\n    if (p==NULL) {\n        len=0;\n        bEnd=p=(const BYTE*)(size_t)32;\n    }\n#endif\n\n    if (len>=32) {\n        const BYTE* const limit = bEnd - 32;\n        U64 v1 = seed + PRIME64_1 + PRIME64_2;\n        U64 v2 = seed + PRIME64_2;\n        U64 v3 = seed + 0;\n        U64 v4 = seed - PRIME64_1;\n\n        do {\n            v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8;\n            v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8;\n            v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8;\n            v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8;\n        } while (p<=limit);\n\n        h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);\n        h64 = XXH64_mergeRound(h64, v1);\n        h64 = XXH64_mergeRound(h64, v2);\n        h64 = XXH64_mergeRound(h64, v3);\n        h64 = XXH64_mergeRound(h64, v4);\n\n    } else {\n        h64  = seed + PRIME64_5;\n    }\n\n    h64 += (U64) len;\n\n    return XXH64_finalize(h64, p, len, endian, align);\n}\n\n\nXXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed)\n{\n#if 0\n    /* Simple version, good for code maintenance, but unfortunately slow for small inputs */\n    XXH64_state_t state;\n    XXH64_reset(&state, seed);\n    XXH64_update(&state, input, len);\n    return XXH64_digest(&state);\n#else\n    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;\n\n    if (XXH_FORCE_ALIGN_CHECK) {\n        if ((((size_t)input) & 7)==0) {  /* Input is aligned, let's leverage the speed advantage */\n            if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\n                return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);\n            else\n                return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);\n    }   }\n\n    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\n        return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);\n    else\n        return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);\n#endif\n}\n\n/*======   Hash Streaming   ======*/\n\nXXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)\n{\n    return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));\n}\nXXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)\n{\n    XXH_free(statePtr);\n    return XXH_OK;\n}\n\nXXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState)\n{\n    memcpy(dstState, srcState, sizeof(*dstState));\n}\n\nXXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed)\n{\n    XXH64_state_t state;   /* using a local state to memcpy() in order to avoid strict-aliasing warnings */\n    memset(&state, 0, sizeof(state));\n    state.v1 = seed + PRIME64_1 + PRIME64_2;\n    state.v2 = seed + PRIME64_2;\n    state.v3 = seed + 0;\n    state.v4 = seed - PRIME64_1;\n     /* do not write into reserved, planned to be removed in a future version */\n    memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved));\n    return XXH_OK;\n}\n\nFORCE_INLINE XXH_errorcode\nXXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian)\n{\n    if (input==NULL)\n#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)\n        return XXH_OK;\n#else\n        return XXH_ERROR;\n#endif\n\n    {   const BYTE* p = (const BYTE*)input;\n        const BYTE* const bEnd = p + len;\n\n        state->total_len += len;\n\n        if (state->memsize + len < 32) {  /* fill in tmp buffer */\n            XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);\n            state->memsize += (U32)len;\n            return XXH_OK;\n        }\n\n        if (state->memsize) {   /* tmp buffer is full */\n            XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize);\n            state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian));\n            state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian));\n            state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian));\n            state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian));\n            p += 32-state->memsize;\n            state->memsize = 0;\n        }\n\n        if (p+32 <= bEnd) {\n            const BYTE* const limit = bEnd - 32;\n            U64 v1 = state->v1;\n            U64 v2 = state->v2;\n            U64 v3 = state->v3;\n            U64 v4 = state->v4;\n\n            do {\n                v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8;\n                v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8;\n                v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8;\n                v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8;\n            } while (p<=limit);\n\n            state->v1 = v1;\n            state->v2 = v2;\n            state->v3 = v3;\n            state->v4 = v4;\n        }\n\n        if (p < bEnd) {\n            XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));\n            state->memsize = (unsigned)(bEnd-p);\n        }\n    }\n\n    return XXH_OK;\n}\n\nXXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len)\n{\n    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;\n\n    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\n        return XXH64_update_endian(state_in, input, len, XXH_littleEndian);\n    else\n        return XXH64_update_endian(state_in, input, len, XXH_bigEndian);\n}\n\nFORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian)\n{\n    U64 h64;\n\n    if (state->total_len >= 32) {\n        U64 const v1 = state->v1;\n        U64 const v2 = state->v2;\n        U64 const v3 = state->v3;\n        U64 const v4 = state->v4;\n\n        h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);\n        h64 = XXH64_mergeRound(h64, v1);\n        h64 = XXH64_mergeRound(h64, v2);\n        h64 = XXH64_mergeRound(h64, v3);\n        h64 = XXH64_mergeRound(h64, v4);\n    } else {\n        h64  = state->v3 /*seed*/ + PRIME64_5;\n    }\n\n    h64 += (U64) state->total_len;\n\n    return XXH64_finalize(h64, state->mem64, (size_t)state->total_len, endian, XXH_aligned);\n}\n\nXXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in)\n{\n    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;\n\n    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)\n        return XXH64_digest_endian(state_in, XXH_littleEndian);\n    else\n        return XXH64_digest_endian(state_in, XXH_bigEndian);\n}\n\n\n/*====== Canonical representation   ======*/\n\nXXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)\n{\n    XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));\n    if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);\n    memcpy(dst, &hash, sizeof(*dst));\n}\n\nXXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)\n{\n    return XXH_readBE64(src);\n}\n\n#endif  /* XXH_NO_LONG_LONG */\n"
  },
  {
    "path": "third-party/l4z/xxhash.h",
    "content": "/*\n   xxHash - Extremely Fast Hash algorithm\n   Header File\n   Copyright (C) 2012-2016, Yann Collet.\n\n   BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)\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\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   You can contact the author at :\n   - xxHash source repository : https://github.com/Cyan4973/xxHash\n*/\n\n/* Notice extracted from xxHash homepage :\n\nxxHash is an extremely fast Hash algorithm, running at RAM speed limits.\nIt also successfully passes all tests from the SMHasher suite.\n\nComparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)\n\nName            Speed       Q.Score   Author\nxxHash          5.4 GB/s     10\nCrapWow         3.2 GB/s      2       Andrew\nMumurHash 3a    2.7 GB/s     10       Austin Appleby\nSpookyHash      2.0 GB/s     10       Bob Jenkins\nSBox            1.4 GB/s      9       Bret Mulvey\nLookup3         1.2 GB/s      9       Bob Jenkins\nSuperFastHash   1.2 GB/s      1       Paul Hsieh\nCityHash64      1.05 GB/s    10       Pike & Alakuijala\nFNV             0.55 GB/s     5       Fowler, Noll, Vo\nCRC32           0.43 GB/s     9\nMD5-32          0.33 GB/s    10       Ronald L. Rivest\nSHA1-32         0.28 GB/s    10\n\nQ.Score is a measure of quality of the hash function.\nIt depends on successfully passing SMHasher test set.\n10 is a perfect score.\n\nA 64-bit version, named XXH64, is available since r35.\nIt offers much better speed, but for 64-bit applications only.\nName     Speed on 64 bits    Speed on 32 bits\nXXH64       13.8 GB/s            1.9 GB/s\nXXH32        6.8 GB/s            6.0 GB/s\n*/\n\n#ifndef XXHASH_H_5627135585666179\n#define XXHASH_H_5627135585666179 1\n\n#if defined (__cplusplus)\nextern \"C\" {\n#endif\n\n\n/* ****************************\n*  Definitions\n******************************/\n#include <stddef.h>   /* size_t */\ntypedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;\n\n\n/* ****************************\n *  API modifier\n ******************************/\n/** XXH_INLINE_ALL (and XXH_PRIVATE_API)\n *  This is useful to include xxhash functions in `static` mode\n *  in order to inline them, and remove their symbol from the public list.\n *  Inlining can offer dramatic performance improvement on small keys.\n *  Methodology :\n *     #define XXH_INLINE_ALL\n *     #include \"xxhash.h\"\n * `xxhash.c` is automatically included.\n *  It's not useful to compile and link it as a separate module.\n */\n#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)\n#  ifndef XXH_STATIC_LINKING_ONLY\n#    define XXH_STATIC_LINKING_ONLY\n#  endif\n#  if defined(__GNUC__)\n#    define XXH_PUBLIC_API static __inline __attribute__((unused))\n#  elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)\n#    define XXH_PUBLIC_API static inline\n#  elif defined(_MSC_VER)\n#    define XXH_PUBLIC_API static __inline\n#  else\n     /* this version may generate warnings for unused static functions */\n#    define XXH_PUBLIC_API static\n#  endif\n#else\n#  define XXH_PUBLIC_API   /* do nothing */\n#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */\n\n/*! XXH_NAMESPACE, aka Namespace Emulation :\n *\n * If you want to include _and expose_ xxHash functions from within your own library,\n * but also want to avoid symbol collisions with other libraries which may also include xxHash,\n *\n * you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library\n * with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values).\n *\n * Note that no change is required within the calling program as long as it includes `xxhash.h` :\n * regular symbol name will be automatically translated by this header.\n */\n#ifdef XXH_NAMESPACE\n#  define XXH_CAT(A,B) A##B\n#  define XXH_NAME2(A,B) XXH_CAT(A,B)\n#  define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber)\n#  define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)\n#  define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)\n#  define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)\n#  define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)\n#  define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)\n#  define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)\n#  define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState)\n#  define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash)\n#  define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical)\n#  define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)\n#  define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)\n#  define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)\n#  define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)\n#  define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)\n#  define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)\n#  define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)\n#  define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)\n#  define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical)\n#endif\n\n\n/* *************************************\n*  Version\n***************************************/\n#define XXH_VERSION_MAJOR    0\n#define XXH_VERSION_MINOR    6\n#define XXH_VERSION_RELEASE  5\n#define XXH_VERSION_NUMBER  (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)\nXXH_PUBLIC_API unsigned XXH_versionNumber (void);\n\n\n/*-**********************************************************************\n*  32-bit hash\n************************************************************************/\ntypedef unsigned int XXH32_hash_t;\n\n/*! XXH32() :\n    Calculate the 32-bit hash of sequence \"length\" bytes stored at memory address \"input\".\n    The memory between input & input+length must be valid (allocated and read-accessible).\n    \"seed\" can be used to alter the result predictably.\n    Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */\nXXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed);\n\n/*======   Streaming   ======*/\ntypedef struct XXH32_state_s XXH32_state_t;   /* incomplete type */\nXXH_PUBLIC_API XXH32_state_t* XXH32_createState(void);\nXXH_PUBLIC_API XXH_errorcode  XXH32_freeState(XXH32_state_t* statePtr);\nXXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state);\n\nXXH_PUBLIC_API XXH_errorcode XXH32_reset  (XXH32_state_t* statePtr, unsigned int seed);\nXXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);\nXXH_PUBLIC_API XXH32_hash_t  XXH32_digest (const XXH32_state_t* statePtr);\n\n/*\n * Streaming functions generate the xxHash of an input provided in multiple segments.\n * Note that, for small input, they are slower than single-call functions, due to state management.\n * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized.\n *\n * XXH state must first be allocated, using XXH*_createState() .\n *\n * Start a new hash by initializing state with a seed, using XXH*_reset().\n *\n * Then, feed the hash state by calling XXH*_update() as many times as necessary.\n * The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.\n *\n * Finally, a hash value can be produced anytime, by using XXH*_digest().\n * This function returns the nn-bits hash as an int or long long.\n *\n * It's still possible to continue inserting input into the hash state after a digest,\n * and generate some new hashes later on, by calling again XXH*_digest().\n *\n * When done, free XXH state space if it was allocated dynamically.\n */\n\n/*======   Canonical representation   ======*/\n\ntypedef struct { unsigned char digest[4]; } XXH32_canonical_t;\nXXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);\nXXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);\n\n/* Default result type for XXH functions are primitive unsigned 32 and 64 bits.\n * The canonical representation uses human-readable write convention, aka big-endian (large digits first).\n * These functions allow transformation of hash result into and from its canonical format.\n * This way, hash values can be written into a file / memory, and remain comparable on different systems and programs.\n */\n\n\n#ifndef XXH_NO_LONG_LONG\n/*-**********************************************************************\n*  64-bit hash\n************************************************************************/\ntypedef unsigned long long XXH64_hash_t;\n\n/*! XXH64() :\n    Calculate the 64-bit hash of sequence of length \"len\" stored at memory address \"input\".\n    \"seed\" can be used to alter the result predictably.\n    This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark).\n*/\nXXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed);\n\n/*======   Streaming   ======*/\ntypedef struct XXH64_state_s XXH64_state_t;   /* incomplete type */\nXXH_PUBLIC_API XXH64_state_t* XXH64_createState(void);\nXXH_PUBLIC_API XXH_errorcode  XXH64_freeState(XXH64_state_t* statePtr);\nXXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state);\n\nXXH_PUBLIC_API XXH_errorcode XXH64_reset  (XXH64_state_t* statePtr, unsigned long long seed);\nXXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length);\nXXH_PUBLIC_API XXH64_hash_t  XXH64_digest (const XXH64_state_t* statePtr);\n\n/*======   Canonical representation   ======*/\ntypedef struct { unsigned char digest[8]; } XXH64_canonical_t;\nXXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);\nXXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src);\n#endif  /* XXH_NO_LONG_LONG */\n\n\n\n#ifdef XXH_STATIC_LINKING_ONLY\n\n/* ================================================================================================\n   This section contains declarations which are not guaranteed to remain stable.\n   They may change in future versions, becoming incompatible with a different version of the library.\n   These declarations should only be used with static linking.\n   Never use them in association with dynamic linking !\n=================================================================================================== */\n\n/* These definitions are only present to allow\n * static allocation of XXH state, on stack or in a struct for example.\n * Never **ever** use members directly. */\n\n#if !defined (__VMS) \\\n  && (defined (__cplusplus) \\\n  || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )\n#   include <stdint.h>\n\nstruct XXH32_state_s {\n   uint32_t total_len_32;\n   uint32_t large_len;\n   uint32_t v1;\n   uint32_t v2;\n   uint32_t v3;\n   uint32_t v4;\n   uint32_t mem32[4];\n   uint32_t memsize;\n   uint32_t reserved;   /* never read nor write, might be removed in a future version */\n};   /* typedef'd to XXH32_state_t */\n\nstruct XXH64_state_s {\n   uint64_t total_len;\n   uint64_t v1;\n   uint64_t v2;\n   uint64_t v3;\n   uint64_t v4;\n   uint64_t mem64[4];\n   uint32_t memsize;\n   uint32_t reserved[2];          /* never read nor write, might be removed in a future version */\n};   /* typedef'd to XXH64_state_t */\n\n# else\n\nstruct XXH32_state_s {\n   unsigned total_len_32;\n   unsigned large_len;\n   unsigned v1;\n   unsigned v2;\n   unsigned v3;\n   unsigned v4;\n   unsigned mem32[4];\n   unsigned memsize;\n   unsigned reserved;   /* never read nor write, might be removed in a future version */\n};   /* typedef'd to XXH32_state_t */\n\n#   ifndef XXH_NO_LONG_LONG  /* remove 64-bit support */\nstruct XXH64_state_s {\n   unsigned long long total_len;\n   unsigned long long v1;\n   unsigned long long v2;\n   unsigned long long v3;\n   unsigned long long v4;\n   unsigned long long mem64[4];\n   unsigned memsize;\n   unsigned reserved[2];     /* never read nor write, might be removed in a future version */\n};   /* typedef'd to XXH64_state_t */\n#    endif\n\n# endif\n\n\n#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)\n#  include \"xxhash.c\"   /* include xxhash function bodies as `static`, for inlining */\n#endif\n\n#endif /* XXH_STATIC_LINKING_ONLY */\n\n\n#if defined (__cplusplus)\n}\n#endif\n\n#endif /* XXHASH_H_5627135585666179 */\n"
  },
  {
    "path": "third-party/nfd/.circleci/config.yml",
    "content": "version: 2.1\n\njobs:\n  build:\n    macos:\n      xcode: 13.3\n    steps:\n      - checkout\n      - run:\n          name: Install dependencies\n          command: |\n            brew install cmake\n      - run: \n          name: Configure\n          command: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX=\"../install\" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS=\"-Wall -Wextra -Werror -pedantic\" -DCMAKE_CXX_FLAGS=\"-Wall -Wextra -Werror -pedantic\" -DNFD_BUILD_TESTS=ON ..\n      - run:\n          name: Build\n          command: cmake --build build --target install\n\nworkflows:\n  main: \n    jobs:\n      - build:\n          name: MacOS 12 - Clang"
  },
  {
    "path": "third-party/nfd/.clang-format",
    "content": "---\nBasedOnStyle: Chromium\nIndentWidth: 4\nBinPackArguments: false\nColumnLimit: 100\nAllowShortIfStatementsOnASingleLine: WithoutElse\nAllowShortLoopsOnASingleLine: true\n---\nLanguage: Cpp\n---\nLanguage: ObjC\n"
  },
  {
    "path": "third-party/nfd/.github/workflows/cmake.yml",
    "content": "name: build\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  clang-format:\n\n    name: ClangFormat check\n    runs-on: ubuntu-latest\n    \n    steps:\n    - name: Checkout\n      uses: actions/checkout@v2\n    - name: Format code\n      # ClangFormat 14 has a bug, which seems to be fixed in ClangFormat 15.  Until GitHub-hosted runners support ClangFormat 15, we will stay at ClangFormat 13.\n      run: find src/ test/ -iname '*.c' -or -iname '*.cpp' -or -iname '*.m' -or -iname '*.mm' -or -iname '*.h' -or -iname '*.hpp' | xargs clang-format-13 -i -style=file\n    - name: Check diff\n      run: git diff --exit-code\n\n  build-ubuntu:\n    \n    name: Ubuntu ${{ matrix.os.name }} - ${{ matrix.compiler.name }}, ${{ matrix.portal.name }}, ${{ matrix.autoappend.name }}, ${{ matrix.shared_lib.name }}, C++${{ matrix.cppstd }}\n    runs-on: ${{ matrix.os.label }}\n\n    strategy:\n      matrix:\n        os: [ {label: ubuntu-latest, name: latest}, {label: ubuntu-20.04, name: 20.04} ]\n        portal: [ {flag: OFF, dep: libgtk-3-dev, name: GTK}, {flag: ON, dep: libdbus-1-dev, name: Portal} ] # The NFD_PORTAL setting defaults to OFF (i.e. uses GTK)\n        autoappend: [ {flag: OFF, name: NoAppendExtn} ] # By default the NFD_PORTAL mode does not append extensions, because it breaks some features of the portal\n        compiler: [ {c: gcc, cpp: g++, name: GCC}, {c: clang, cpp: clang++, name: Clang} ] # The default compiler is gcc/g++\n        cppstd: [23, 11]\n        shared_lib: [ {flag: OFF, name: Static} ]\n        include:\n        - os: {label: ubuntu-latest, name: latest}\n          portal: {flag: ON, dep: libdbus-1-dev, name: Portal}\n          autoappend: {flag: ON, name: AutoAppendExtn}\n          compiler: {c: gcc, cpp: g++, name: GCC}\n          cppstd: 11\n          shared_lib: {flag: OFF, name: Static}\n        - os: {label: ubuntu-latest, name: latest}\n          portal: {flag: ON, dep: libdbus-1-dev, name: Portal}\n          autoappend: {flag: ON, name: AutoAppendExtn}\n          compiler: {c: clang, cpp: clang++, name: Clang}\n          cppstd: 11\n          shared_lib: {flag: OFF, name: Static}\n        - os: {label: ubuntu-latest, name: latest}\n          portal: {flag: ON, dep: libdbus-1-dev, name: Portal}\n          autoappend: {flag: ON, name: NoAppendExtn}\n          compiler: {c: gcc, cpp: g++, name: GCC}\n          cppstd: 11\n          shared_lib: {flag: ON, name: Shared}\n\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v2\n    - name: Installing Dependencies\n      run: sudo apt-get update && sudo apt-get install ${{ matrix.portal.dep }}\n    - name: Configure\n      run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX=\"../install\" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=${{ matrix.compiler.c }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cpp }} -DCMAKE_CXX_STANDARD=${{ matrix.cppstd }} -DCMAKE_C_FLAGS=\"-Wall -Wextra -Werror -pedantic\" -DCMAKE_CXX_FLAGS=\"-Wall -Wextra -Werror -pedantic\" -DNFD_PORTAL=${{ matrix.portal.flag }} -DNFD_APPEND_EXTENSION=${{ matrix.autoappend.flag }} -DBUILD_SHARED_LIBS=${{ matrix.shared_lib.flag }} -DNFD_BUILD_TESTS=ON ..\n    - name: Build\n      run: cmake --build build --target install\n    - name: Upload test binaries\n      uses: actions/upload-artifact@v2\n      with:\n        name: Ubuntu ${{ matrix.os.name }} - ${{ matrix.compiler.name }}, ${{ matrix.portal.name }}, ${{ matrix.autoappend.name }}, ${{ matrix.shared_lib.name }}, C++${{ matrix.cppstd }}\n        path: |\n          build/src/*\n          build/test/*\n  \n  build-macos-clang:\n\n    name: MacOS ${{ matrix.os.name }} - Clang, ${{ matrix.shared_lib.name }}\n    runs-on: ${{ matrix.os.label }}\n\n    strategy:\n      matrix:\n        os: [ {label: macos-latest, name: latest}, {label: macos-11, name: 11} ]\n        shared_lib: [ {flag: OFF, name: Static} ]\n        include:\n        - os: {label: macos-latest, name: latest}\n          shared_lib: {flag: ON, name: Shared}\n    \n    steps:\n    - name: Checkout\n      uses: actions/checkout@v2\n    - name: Configure\n      run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX=\"../install\" -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS=\"-Wall -Wextra -Werror -pedantic\" -DCMAKE_CXX_FLAGS=\"-Wall -Wextra -Werror -pedantic\" -DBUILD_SHARED_LIBS=${{ matrix.shared_lib.flag }} -DNFD_BUILD_TESTS=ON ..\n    - name: Build\n      run: cmake --build build --target install\n    - name: Upload test binaries\n      uses: actions/upload-artifact@v2\n      with:\n        name: MacOS ${{ matrix.os.name }} - Clang, ${{ matrix.shared_lib.name }}\n        path: |\n          build/src/*\n          build/test/*\n  \n  build-windows-msvc:\n\n    name: Windows latest - MSVC, ${{ matrix.shared_lib.name }}\n    runs-on: windows-latest\n    \n    strategy:\n      matrix:\n        shared_lib: [ {flag: OFF, name: Static}, {flag: ON, name: Shared} ]\n    \n    steps:\n    - name: Checkout\n      uses: actions/checkout@v2\n    - name: Configure\n      run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX=\"../install\" -DBUILD_SHARED_LIBS=${{ matrix.shared_lib.flag }} -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=${{ matrix.shared_lib.flag }} -DNFD_BUILD_TESTS=ON ..\n    - name: Build\n      run: cmake --build build --target install --config Release\n    - name: Upload test binaries\n      uses: actions/upload-artifact@v2\n      with:\n        name: Windows latest - MSVC, ${{ matrix.shared_lib.name }}\n        path: |\n          build/src/Release/*\n          build/test/Release/*\n  \n  build-windows-clang:\n\n    name: Windows latest - Clang, Static\n    runs-on: windows-latest\n    \n    steps:\n    - name: Checkout\n      uses: actions/checkout@v2\n    - name: Configure\n      run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX=\"../install\" -T ClangCL -DNFD_BUILD_TESTS=ON ..\n    - name: Build\n      run: cmake --build build --target install --config Release\n    - name: Upload test binaries\n      uses: actions/upload-artifact@v2\n      with:\n        name: Windows latest - Clang, Static\n        path: |\n          build/src/Release/*\n          build/test/Release/*\n  \n  build-windows-mingw:\n\n    name: Windows latest - MinGW, Static\n    runs-on: windows-latest\n\n    defaults:\n      run:\n        shell: msys2 {0}\n\n    steps:\n    - name: Checkout\n      uses: actions/checkout@v2\n    - name: Set up MinGW-w64\n      uses: msys2/setup-msys2@v2\n      with:\n        path-type: minimal\n        install: >-\n          base-devel\n          mingw-w64-x86_64-gcc\n          mingw-w64-x86_64-cmake\n    - name: Configure\n      run: mkdir build && mkdir install && cd build && cmake -DCMAKE_INSTALL_PREFIX=\"../install\" -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS=\"-Wall -Wextra -Werror -pedantic\" -DCMAKE_CXX_FLAGS=\"-Wall -Wextra -Werror -pedantic\" -DNFD_BUILD_TESTS=ON ..\n    - name: Build\n      run: cmake --build build --target install\n    - name: Upload test binaries\n      uses: actions/upload-artifact@v2\n      with:\n        name: Windows latest - MinGW, Static\n        path: |\n          build/src/*\n          build/test/*\n"
  },
  {
    "path": "third-party/nfd/.gitignore",
    "content": "# VS CMake default output\n/.vs/\n/out/\n/CMakeSettings.json\n\n# Mac OS X rubbish\n.DS_Store\n"
  },
  {
    "path": "third-party/nfd/include/nfd.h",
    "content": "/*\n  Native File Dialog\n\n  User API\n\n  http://www.frogtoss.com/labs\n */\n\n\n#ifndef _NFD_H\n#define _NFD_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stddef.h>\n\n/* denotes UTF-8 char */\ntypedef char nfdchar_t;\n\n/* opaque data structure -- see NFD_PathSet_* */\ntypedef struct {\n    nfdchar_t *buf;\n    size_t *indices; /* byte offsets into buf */\n    size_t count;    /* number of indices into buf */\n}nfdpathset_t;\n\ntypedef enum {\n    NFD_ERROR,       /* programmatic error */\n    NFD_OKAY,        /* user pressed okay, or successful return */\n    NFD_CANCEL       /* user pressed cancel */\n}nfdresult_t;\n    \n\n/* nfd_<targetplatform>.c */\n\n/* single file open dialog */    \nnfdresult_t NFD_OpenDialog( const nfdchar_t *filterList,\n                            const nfdchar_t *defaultPath,\n                            nfdchar_t **outPath );\n\n/* multiple file open dialog */    \nnfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,\n                                    const nfdchar_t *defaultPath,\n                                    nfdpathset_t *outPaths );\n\n/* save dialog */\nnfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,\n                            const nfdchar_t *defaultPath,\n                            nfdchar_t **outPath );\n\n\n/* select folder dialog */\nnfdresult_t NFD_PickFolder( const nfdchar_t *defaultPath,\n                            nfdchar_t **outPath);\n\n/* nfd_common.c */\n\n/* get last error -- set when nfdresult_t returns NFD_ERROR */\nconst char *NFD_GetError( void );\n/* get the number of entries stored in pathSet */\nsize_t      NFD_PathSet_GetCount( const nfdpathset_t *pathSet );\n/* Get the UTF-8 path at offset index */\nnfdchar_t  *NFD_PathSet_GetPath( const nfdpathset_t *pathSet, size_t index );\n/* Free the pathSet */    \nvoid        NFD_PathSet_Free( nfdpathset_t *pathSet );\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "third-party/pugixml/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5)\r\n\r\n# Policy configuration; this *MUST* be specified before project is defined\r\nif(POLICY CMP0091)\r\n    cmake_policy(SET CMP0091 NEW) # Enables use of MSVC_RUNTIME_LIBRARY\r\nendif()\r\n\r\nproject(pugixml VERSION 1.14 LANGUAGES CXX)\r\n\r\ninclude(CMakePackageConfigHelpers)\r\ninclude(CMakeDependentOption)\r\ninclude(GNUInstallDirs)\r\ninclude(CTest)\r\n\r\ncmake_dependent_option(PUGIXML_USE_VERSIONED_LIBDIR\r\n  \"Use a private subdirectory to install the headers and libraries\" OFF\r\n  \"CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR\" OFF)\r\n\r\ncmake_dependent_option(PUGIXML_USE_POSTFIX\r\n  \"Use separate postfix for each configuration to make sure you can install multiple build outputs\" OFF\r\n  \"CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR\" OFF)\r\n\r\ncmake_dependent_option(PUGIXML_STATIC_CRT\r\n  \"Use static MSVC RT libraries\" OFF\r\n  \"MSVC\" OFF)\r\n\r\ncmake_dependent_option(PUGIXML_BUILD_TESTS\r\n  \"Build pugixml tests\" OFF\r\n  \"BUILD_TESTING;CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR\" OFF)\r\n\r\n# Custom build defines\r\nset(PUGIXML_BUILD_DEFINES CACHE STRING \"Build defines for custom options\")\r\nseparate_arguments(PUGIXML_BUILD_DEFINES)\r\n\r\n# Technically not needed for this file. This is builtin CMAKE global variable.\r\noption(BUILD_SHARED_LIBS \"Build shared instead of static library\" OFF) \r\n\r\n# Expose option to build PUGIXML as static as well when the global BUILD_SHARED_LIBS variable is set\r\ncmake_dependent_option(PUGIXML_BUILD_SHARED_AND_STATIC_LIBS\r\n  \"Build both shared and static libraries\" OFF\r\n  \"BUILD_SHARED_LIBS\" OFF)\r\n\r\n# Expose options from the pugiconfig.hpp\r\noption(PUGIXML_WCHAR_MODE \"Enable wchar_t mode\" OFF)\r\noption(PUGIXML_COMPACT \"Enable compact mode\" OFF)\r\n\r\n# Advanced options from pugiconfig.hpp\r\noption(PUGIXML_NO_XPATH \"Disable XPath\" OFF)\r\noption(PUGIXML_NO_STL \"Disable STL\" OFF)\r\noption(PUGIXML_NO_EXCEPTIONS \"Disable Exceptions\" OFF)\r\nmark_as_advanced(PUGIXML_NO_XPATH PUGIXML_NO_STL PUGIXML_NO_EXCEPTIONS)\r\n\r\nset(PUGIXML_PUBLIC_DEFINITIONS\r\n  $<$<BOOL:${PUGIXML_WCHAR_MODE}>:PUGIXML_WCHAR_MODE>\r\n  $<$<BOOL:${PUGIXML_COMPACT}>:PUGIXML_COMPACT>\r\n  $<$<BOOL:${PUGIXML_NO_XPATH}>:PUGIXML_NO_XPATH>\r\n  $<$<BOOL:${PUGIXML_NO_STL}>:PUGIXML_NO_STL>\r\n  $<$<BOOL:${PUGIXML_NO_EXCEPTIONS}>:PUGIXML_NO_EXCEPTIONS>)\r\n\r\n# This is used to backport a CMake 3.15 feature, but is also forwards compatible\r\nif (NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)\r\n  set(CMAKE_MSVC_RUNTIME_LIBRARY\r\n    MultiThreaded$<$<CONFIG:Debug>:Debug>$<$<NOT:$<BOOL:${PUGIXML_STATIC_CRT}>>:DLL>)\r\nendif()\r\n\r\nif (NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED)\r\n  set(CMAKE_CXX_STANDARD_REQUIRED ON)\r\nendif()\r\n\r\nif (NOT DEFINED CMAKE_CXX_STANDARD)\r\n  set(CMAKE_CXX_STANDARD 11)\r\nendif()\r\n\r\nif (PUGIXML_USE_POSTFIX)\r\n  set(CMAKE_RELWITHDEBINFO_POSTFIX _r)\r\n  set(CMAKE_MINSIZEREL_POSTFIX _m)\r\n  set(CMAKE_DEBUG_POSTFIX _d)\r\nendif()\r\n\r\nif (CMAKE_VERSION VERSION_LESS 3.15)\r\n  set(msvc-rt $<TARGET_PROPERTY:MSVC_RUNTIME_LIBRARY>)\r\n\r\n  set(msvc-rt-mtd-shared $<STREQUAL:${msvc-rt},MultiThreadedDebugDLL>)\r\n  set(msvc-rt-mtd-static $<STREQUAL:${msvc-rt},MultiThreadedDebug>)\r\n  set(msvc-rt-mt-shared $<STREQUAL:${msvc-rt},MultiThreadedDLL>)\r\n  set(msvc-rt-mt-static $<STREQUAL:${msvc-rt},MultiThreaded>)\r\n  unset(msvc-rt)\r\n\r\n  set(msvc-rt-mtd-shared $<${msvc-rt-mtd-shared}:-MDd>)\r\n  set(msvc-rt-mtd-static $<${msvc-rt-mtd-static}:-MTd>)\r\n  set(msvc-rt-mt-shared $<${msvc-rt-mt-shared}:-MD>)\r\n  set(msvc-rt-mt-static $<${msvc-rt-mt-static}:-MT>)\r\nendif()\r\n\r\nset(versioned-dir $<$<BOOL:${PUGIXML_USE_VERSIONED_LIBDIR}>:/pugixml-${PROJECT_VERSION}>)\r\n\r\nset(libs)\r\n\r\nif (BUILD_SHARED_LIBS)\r\n  add_library(pugixml-shared SHARED\r\n    ${PROJECT_SOURCE_DIR}/scripts/pugixml_dll.rc\r\n    ${PROJECT_SOURCE_DIR}/src/pugixml.cpp)\r\n  add_library(pugixml::shared ALIAS pugixml-shared)\r\n  list(APPEND libs pugixml-shared)\r\n  string(CONCAT pugixml.msvc $<OR:\r\n    $<STREQUAL:${CMAKE_CXX_COMPILER_FRONTEND_VARIANT},MSVC>,\r\n    $<CXX_COMPILER_ID:MSVC>\r\n  >)\r\n\r\n  set_property(TARGET pugixml-shared PROPERTY EXPORT_NAME shared)\r\n  target_include_directories(pugixml-shared\r\n    PUBLIC\r\n      $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>)\r\n  target_compile_definitions(pugixml-shared\r\n    PUBLIC\r\n      ${PUGIXML_BUILD_DEFINES}\r\n      ${PUGIXML_PUBLIC_DEFINITIONS}\r\n    PRIVATE\r\n      PUGIXML_API=$<IF:${pugixml.msvc},__declspec\\(dllexport\\),__attribute__\\(\\(visibility\\(\"default\"\\)\\)\\)>\r\n    )\r\n  target_compile_options(pugixml-shared\r\n    PRIVATE\r\n      ${msvc-rt-mtd-shared}\r\n      ${msvc-rt-mtd-static}\r\n      ${msvc-rt-mt-shared}\r\n      ${msvc-rt-mt-static})\r\nendif()\r\n\r\nif (NOT BUILD_SHARED_LIBS OR PUGIXML_BUILD_SHARED_AND_STATIC_LIBS)\r\n  add_library(pugixml-static STATIC\r\n    ${PROJECT_SOURCE_DIR}/src/pugixml.cpp)\r\n  add_library(pugixml::static ALIAS pugixml-static)\r\n  list(APPEND libs pugixml-static)\r\n\r\n  set_property(TARGET pugixml-static PROPERTY EXPORT_NAME static)\r\n  target_include_directories(pugixml-static\r\n    PUBLIC\r\n      $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>)\r\n  target_compile_definitions(pugixml-static\r\n    PUBLIC\r\n      ${PUGIXML_BUILD_DEFINES}\r\n      ${PUGIXML_PUBLIC_DEFINITIONS})\r\n  target_compile_options(pugixml-static\r\n    PRIVATE\r\n      ${msvc-rt-mtd-shared}\r\n      ${msvc-rt-mtd-static}\r\n      ${msvc-rt-mt-shared}\r\n      ${msvc-rt-mt-static})\r\nendif()\r\n\r\nif (BUILD_SHARED_LIBS)\r\n  set(pugixml-alias pugixml-shared)\r\nelse()\r\n  set(pugixml-alias pugixml-static)\r\nendif()\r\nadd_library(pugixml INTERFACE)\r\ntarget_link_libraries(pugixml INTERFACE ${pugixml-alias})\r\nadd_library(pugixml::pugixml ALIAS pugixml)\r\n\r\nset_target_properties(${libs}\r\n  PROPERTIES\r\n    MSVC_RUNTIME_LIBRARY ${CMAKE_MSVC_RUNTIME_LIBRARY}\r\n    EXCLUDE_FROM_ALL ON\r\n    POSITION_INDEPENDENT_CODE ON\r\n    SOVERSION ${PROJECT_VERSION_MAJOR}\r\n    VERSION ${PROJECT_VERSION}\r\n    OUTPUT_NAME pugixml)\r\n\r\nset_target_properties(${libs}\r\n  PROPERTIES\r\n    EXCLUDE_FROM_ALL OFF)\r\nset(install-targets pugixml ${libs})\r\n\r\nconfigure_package_config_file(\r\n  \"${PROJECT_SOURCE_DIR}/scripts/pugixml-config.cmake.in\"\r\n  \"${PROJECT_BINARY_DIR}/pugixml-config.cmake\"\r\n  INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}\r\n  NO_CHECK_REQUIRED_COMPONENTS_MACRO\r\n  NO_SET_AND_CHECK_MACRO)\r\n\r\nwrite_basic_package_version_file(\r\n  \"${PROJECT_BINARY_DIR}/pugixml-config-version.cmake\"\r\n  COMPATIBILITY SameMajorVersion)\r\n\r\nif (PUGIXML_USE_POSTFIX)\r\n  if(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)\r\n    set(LIB_POSTFIX ${CMAKE_RELWITHDEBINFO_POSTFIX})\r\n  elseif(CMAKE_BUILD_TYPE MATCHES MinSizeRel)\r\n    set(LIB_POSTFIX ${CMAKE_MINSIZEREL_POSTFIX})\r\n  elseif(CMAKE_BUILD_TYPE MATCHES Debug)\r\n    set(LIB_POSTFIX ${CMAKE_DEBUG_POSTFIX})\r\n  endif()\r\nendif()\r\n\r\nconfigure_file(scripts/pugixml.pc.in pugixml.pc @ONLY)\r\n\r\nif (NOT DEFINED PUGIXML_RUNTIME_COMPONENT)\r\n  set(PUGIXML_RUNTIME_COMPONENT Runtime)\r\nendif()\r\n\r\nif (NOT DEFINED PUGIXML_LIBRARY_COMPONENT)\r\n  set(PUGIXML_LIBRARY_COMPONENT Library)\r\nendif()\r\n\r\nif (NOT DEFINED PUGIXML_DEVELOPMENT_COMPONENT)\r\n  set(PUGIXML_DEVELOPMENT_COMPONENT Development)\r\nendif()\r\n\r\nset(namelink-component)\r\nif (NOT CMAKE_VERSION VERSION_LESS 3.12)\r\n  set(namelink-component NAMELINK_COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT})\r\nendif()\r\ninstall(TARGETS ${install-targets}\r\n  EXPORT pugixml-targets\r\n  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${PUGIXML_RUNTIME_COMPONENT}\r\n  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${PUGIXML_LIBRARY_COMPONENT} ${namelink-component}\r\n  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT}\r\n  INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}${versioned-dir})\r\n\r\ninstall(EXPORT pugixml-targets\r\n  NAMESPACE pugixml::\r\n  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pugixml COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT})\r\n\r\nexport(EXPORT pugixml-targets\r\n  NAMESPACE pugixml::)\r\n\r\ninstall(FILES\r\n  \"${PROJECT_BINARY_DIR}/pugixml-config-version.cmake\"\r\n  \"${PROJECT_BINARY_DIR}/pugixml-config.cmake\"\r\n  DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pugixml COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT})\r\n\r\ninstall(FILES ${PROJECT_BINARY_DIR}/pugixml.pc\r\n  DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT})\r\n\r\ninstall(\r\n  FILES\r\n    \"${PROJECT_SOURCE_DIR}/src/pugiconfig.hpp\"\r\n    \"${PROJECT_SOURCE_DIR}/src/pugixml.hpp\"\r\n  DESTINATION\r\n    ${CMAKE_INSTALL_INCLUDEDIR}${versioned-dir} COMPONENT ${PUGIXML_DEVELOPMENT_COMPONENT})\r\n\r\nif (PUGIXML_BUILD_TESTS)\r\n  set(fuzz-pattern \"tests/fuzz_*.cpp\")\r\n  set(test-pattern \"tests/*.cpp\")\r\n  if (CMAKE_VERSION VERSION_GREATER 3.11)\r\n    list(INSERT fuzz-pattern 0 CONFIGURE_DEPENDS)\r\n    list(INSERT test-pattern 0 CONFIGURE_DEPENDS)\r\n  endif()\r\n  file(GLOB test-sources ${test-pattern})\r\n  file(GLOB fuzz-sources ${fuzz-pattern})\r\n  list(REMOVE_ITEM test-sources ${fuzz-sources})\r\n\r\n  add_custom_target(check\r\n    COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure)\r\n\r\n  add_executable(pugixml-check ${test-sources})\r\n  add_test(NAME pugixml::test\r\n    COMMAND pugixml-check\r\n    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})\r\n  add_dependencies(check pugixml-check)\r\n  target_link_libraries(pugixml-check\r\n    PRIVATE\r\n      pugixml::pugixml)\r\nendif()\r\n"
  },
  {
    "path": "third-party/pugixml/LICENSE.md",
    "content": "MIT License\r\n\r\nCopyright (c) 2006-2023 Arseny Kapoulkine\r\n\r\nPermission is hereby granted, free of charge, to any person\r\nobtaining a copy of this software and associated documentation\r\nfiles (the \"Software\"), to deal in the Software without\r\nrestriction, including without limitation the rights to use,\r\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the\r\nSoftware is furnished to do so, subject to the following\r\nconditions:\r\n\r\nThe above copyright notice and this permission notice shall be\r\nincluded in all copies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\r\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\r\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\r\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\r\nOTHER DEALINGS IN THE SOFTWARE.\r\n"
  },
  {
    "path": "third-party/pugixml/readme.txt",
    "content": "pugixml 1.14 - an XML processing library\r\n\r\nCopyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)\r\nReport bugs and download new versions at https://pugixml.org/\r\n\r\nThis is the distribution of pugixml, which is a C++ XML processing library,\r\nwhich consists of a DOM-like interface with rich traversal/modification\r\ncapabilities, an extremely fast XML parser which constructs the DOM tree from\r\nan XML file/buffer, and an XPath 1.0 implementation for complex data-driven\r\ntree queries. Full Unicode support is also available, with Unicode interface\r\nvariants and conversions between different Unicode encodings (which happen\r\nautomatically during parsing/saving).\r\n\r\nThe distribution contains the following folders:\r\n\r\n\tdocs/ - documentation\r\n\t\tdocs/samples - pugixml usage examples\r\n\t\tdocs/quickstart.html - quick start guide\r\n\t\tdocs/manual.html - complete manual\r\n\r\n\tscripts/ - project files for IDE/build systems\r\n\r\n\tsrc/ - header and source files\r\n\r\n\treadme.txt - this file.\r\n\r\nThis library is distributed under the MIT License:\r\n\r\nCopyright (c) 2006-2023 Arseny Kapoulkine\r\n\r\nPermission is hereby granted, free of charge, to any person\r\nobtaining a copy of this software and associated documentation\r\nfiles (the \"Software\"), to deal in the Software without\r\nrestriction, including without limitation the rights to use,\r\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the\r\nSoftware is furnished to do so, subject to the following\r\nconditions:\r\n\r\nThe above copyright notice and this permission notice shall be\r\nincluded in all copies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\r\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\r\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\r\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\r\nOTHER DEALINGS IN THE SOFTWARE.\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/cocoapods_push.sh",
    "content": "#!/bin/bash\r\n\r\n#Push to igagis repo for now\r\npod repo push igagis pugixml.podspec --use-libraries --verbose\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/natvis/pugixml.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=\"pugi::xml_node\">\r\n    <DisplayString Condition=\"_root\">{_root}</DisplayString>\r\n    <DisplayString Condition=\"!_root\">none</DisplayString>\r\n    <Expand>\r\n      <ExpandedItem Condition=\"_root\">_root</ExpandedItem>\r\n    </Expand>\r\n  </Type>\r\n  \r\n  <Type Name=\"pugi::xml_node_struct\">\r\n    <DisplayString Condition=\"name &amp;&amp; value\">{(pugi::xml_node_type)(header &amp; 0xf),en} name={name,na} value={value,na}</DisplayString>\r\n    <DisplayString Condition=\"name\">{(pugi::xml_node_type)(header &amp; 0xf),en} name={name,na}</DisplayString>\r\n    <DisplayString Condition=\"value\">{(pugi::xml_node_type)(header &amp; 0xf),en} value={value,na}</DisplayString>\r\n    <DisplayString>{(pugi::xml_node_type)(header &amp; 0xf),en}</DisplayString>\r\n    <Expand>\r\n      <Item Name=\"value\" Condition=\"value\">value,na</Item>\r\n      <Synthetic Name=\"attributes\" Condition=\"first_attribute\">\r\n        <Expand>\r\n          <CustomListItems>\r\n            <Variable Name=\"curr\" InitialValue=\"first_attribute\" />\r\n\r\n            <Loop Condition=\"curr\">\r\n              <Item Name=\"{curr->name,na}\">curr,view(child)na</Item>\r\n              <Exec>curr = curr->next_attribute</Exec>\r\n            </Loop>\r\n          </CustomListItems>\r\n        </Expand>\r\n      </Synthetic>\r\n      <LinkedListItems>\r\n        <HeadPointer>first_child</HeadPointer>\r\n        <NextPointer>next_sibling</NextPointer>\r\n        <ValueNode>this,na</ValueNode>\r\n      </LinkedListItems>\r\n    </Expand>\r\n  </Type>\r\n  \r\n  <Type Name=\"pugi::xml_attribute\">\r\n    <DisplayString Condition=\"_attr\">{_attr}</DisplayString>\r\n    <DisplayString Condition=\"!_attr\">none</DisplayString>\r\n    <Expand>\r\n      <ExpandedItem Condition=\"_attr\">_attr</ExpandedItem>\r\n    </Expand>\r\n  </Type>\r\n  \r\n  <Type Name=\"pugi::xml_attribute_struct\">\r\n    <DisplayString ExcludeView=\"child\">{name,na} = {value,na}</DisplayString>\r\n    <DisplayString>{value,na}</DisplayString>\r\n    <Expand>\r\n      <Item Name=\"name\">name,na</Item>\r\n      <Item Name=\"value\">value,na</Item>\r\n    </Expand>\r\n  </Type>\r\n  \r\n  <Type Name=\"pugi::xpath_node\">\r\n    <DisplayString Condition=\"_node._root &amp;&amp; _attribute._attr\">{_node,na} \"{_attribute._attr->name,na}\"=\"{_attribute._attr->value,na}\"</DisplayString>\r\n    <DisplayString Condition=\"_node._root\">{_node,na}</DisplayString>\r\n    <DisplayString Condition=\"_attribute._attr\">{_attribute}</DisplayString>\r\n    <DisplayString>empty</DisplayString>\r\n    <Expand HideRawView=\"1\">\r\n      <ExpandedItem Condition=\"_node._root &amp;&amp; !_attribute._attr\">_node</ExpandedItem>\r\n      <ExpandedItem Condition=\"!_node._root &amp;&amp; _attribute._attr\">_attribute</ExpandedItem>\r\n      <Item Name=\"node\" Condition=\"_node._root &amp;&amp; _attribute._attr\">_node,na</Item>\r\n      <Item Name=\"attribute\" Condition=\"_node._root &amp;&amp; _attribute._attr\">_attribute,na</Item>\r\n    </Expand>\r\n  </Type>\r\n  \r\n  <Type Name=\"pugi::xpath_node_set\">\r\n    <Expand>\r\n      <Item Name=\"type\">_type</Item>\r\n      <ArrayItems>\r\n        <Size>_end - _begin</Size>\r\n        <ValuePointer>_begin</ValuePointer>\r\n      </ArrayItems>\r\n    </Expand>\r\n  </Type>\r\n</AutoVisualizer>"
  },
  {
    "path": "third-party/pugixml/scripts/natvis/pugixml_compact.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=\"pugi::xml_node\">\r\n    <DisplayString Condition=\"_root\">{_root}</DisplayString>\r\n    <DisplayString Condition=\"!_root\">none</DisplayString>\r\n    <Expand>\r\n      <ExpandedItem Condition=\"_root\">_root</ExpandedItem>\r\n    </Expand>\r\n  </Type>\r\n  \r\n  <Type Name=\"pugi::xml_attribute\">\r\n    <DisplayString Condition=\"_attr\">{_attr}</DisplayString>\r\n    <DisplayString Condition=\"!_attr\">none</DisplayString>\r\n    <Expand>\r\n      <ExpandedItem Condition=\"_attr\">_attr</ExpandedItem>\r\n    </Expand>\r\n  </Type>\r\n  \r\n  <Type Name=\"pugi::xml_node_struct\">\r\n    <Expand>\r\n      <Item Name=\"type\">(pugi::xml_node_type)(header._flags &amp; 15)</Item>\r\n      <Item Name=\"name\" Condition=\"name._data\">name,na</Item>\r\n      <Item Name=\"value\" Condition=\"value._data\">value,na</Item>\r\n      \r\n      <Synthetic Name=\"attributes\" Condition=\"first_attribute._data\">\r\n        <DisplayString>...</DisplayString>\r\n        <Expand>\r\n          <CustomListItems>\r\n            <Variable Name=\"attribute_this\" InitialValue=\"(size_t)&amp;first_attribute\" />\r\n            <Variable Name=\"attribute_data\" InitialValue=\"first_attribute._data\" />\r\n            <Variable Name=\"attribute_data_copy\" InitialValue=\"attribute_data\" />\r\n            \r\n            <!-- first_attribute struct template arguments -->\r\n            <Variable Name=\"attribute_T1\" InitialValue=\"11\" />\r\n            <Variable Name=\"attribute_T2\" InitialValue=\"0\" />\r\n\r\n            <Variable Name=\"compact_alignment_log2\" InitialValue=\"2\" />\r\n            <Variable Name=\"compact_alignment\" InitialValue=\"1 &lt;&lt; compact_alignment_log2\" />\r\n            \r\n            <!-- compact_get_page() -->\r\n            <Variable Name=\"_page\" InitialValue=\"*(char*)(attribute_this - attribute_T1)\" />\r\n            <Variable Name=\"page\" InitialValue=\"((attribute_this - attribute_T1 - (_page &lt;&lt; compact_alignment_log2)) - *(unsigned*)(attribute_this - attribute_T1 - (_page &lt;&lt; compact_alignment_log2)))\" />\r\n\r\n            <!-- page->allocator->_hash -->\r\n            <Variable Name=\"allocator\" InitialValue=\"*(size_t*)page\" />\r\n            <Variable Name=\"_hash\" InitialValue=\"*(size_t*)(allocator + 2 * sizeof(size_t))\" /><!--2 pointer offsetof(allocator, _hash)-->\r\n            <Variable Name=\"_items\" InitialValue=\"*(size_t*)_hash\" />\r\n            <Variable Name=\"_capacity\" InitialValue=\"*((size_t*)_hash + 1)\" /><!--1 pointer offsetof(_hash, _capacity)-->\r\n            <Variable Name=\"_count\" InitialValue=\"*((size_t*)_hash + 2)\" /><!--2 pointer offsetof(_hash, _count)-->\r\n\r\n            <!-- find() prolog -->\r\n            <Variable Name=\"hashmod\" InitialValue=\"_capacity - 1\" />\r\n\r\n            <Variable Name=\"h\" InitialValue=\"(unsigned)attribute_this\" />\r\n            <Variable Name=\"bucket\" InitialValue=\"0\" />\r\n\r\n            <Variable Name=\"probe\" InitialValue=\"0\" />\r\n            <Variable Name=\"probe_item\" InitialValue=\"(size_t*)0\" />\r\n\r\n            <Variable Name=\"attribute_real\" InitialValue=\"(pugi::xml_attribute_struct*)0\" />\r\n\r\n            <!-- if _data < 255 -->\r\n            <Variable Name=\"attribute_short\" InitialValue=\"(pugi::xml_attribute_struct*)(((size_t)attribute_this &amp; ~(compact_alignment - 1)) + (attribute_data - 1 + attribute_T2) * compact_alignment)\" />\r\n\r\n            <Variable Name=\"number\" InitialValue=\"0\" />\r\n\r\n            <!-- Loop over all attributes -->\r\n            <Loop Condition=\"attribute_this &amp;&amp; attribute_data\">\r\n              <!-- find() hash -->\r\n              <Exec>h = h ^ (h >> 16)</Exec>\r\n              <Exec>h = h * (0x85ebca6bu)</Exec>\r\n              <Exec>h = h ^ (h >> 13)</Exec>\r\n              <Exec>h = h * (0xc2b2ae35u)</Exec>\r\n              <Exec>h = h ^ (h >> 16)</Exec>\r\n\r\n              <Exec>bucket = h &amp; hashmod</Exec>\r\n\r\n              <!-- find() loop -->\r\n              <Loop Condition=\"probe &lt;= hashmod &amp;&amp;_capacity\">\r\n                <Exec>probe_item = (size_t*)_items + bucket * 2</Exec><!--2 pointer sizeof(item_t)-->\r\n\r\n                <If Condition=\"*probe_item == attribute_this || *probe_item == 0\">\r\n                  <Exec>attribute_real = *(pugi::xml_attribute_struct**)(probe_item + 1)</Exec><!--1 pointer offsetof(item_t, value)-->\r\n                  <Break/>\r\n                </If>\r\n\r\n                <Exec>bucket = (bucket + probe + 1) &amp; hashmod</Exec>\r\n                <Exec>probe++</Exec>\r\n              </Loop>\r\n\r\n              <Exec>attribute_data_copy = attribute_data</Exec>\r\n\r\n              <If Condition=\"attribute_data_copy &gt;= 255 &amp;&amp; attribute_real\">\r\n                <Item Name=\"[{number}]\">*attribute_real,view(child)</Item>\r\n                <Exec>attribute_this = (size_t)&amp;(*attribute_real).next_attribute</Exec>\r\n                <Exec>attribute_data = (*attribute_real).next_attribute._data</Exec>\r\n              </If>\r\n              <If Condition=\"attribute_data_copy &lt; 255 &amp;&amp; attribute_short\">\r\n                <Item Name=\"[{number}]\">*attribute_short,view(child)</Item>\r\n                <Exec>attribute_this = (size_t)&amp;(*attribute_short).next_attribute</Exec>\r\n                <Exec>attribute_data = (*attribute_short).next_attribute._data</Exec>\r\n              </If>\r\n\r\n              <!-- next_attribute struct template arguments -->\r\n              <Exec>attribute_T1 = 7</Exec>\r\n              <Exec>attribute_T2 = 0</Exec>\r\n\r\n              <!-- find() prolog again -->\r\n              <Exec>h = (unsigned)attribute_this</Exec>\r\n              <Exec>bucket = 0</Exec>\r\n\r\n              <Exec>probe = 0</Exec>\r\n              <Exec>probe_item = (size_t*)0</Exec>\r\n\r\n              <Exec>attribute_real = (pugi::xml_attribute_struct*)0</Exec>\r\n              <Exec>attribute_short = (pugi::xml_attribute_struct*)(((size_t)attribute_this &amp; ~(compact_alignment - 1)) + (attribute_data - 1 + attribute_T2) * compact_alignment)</Exec>\r\n\r\n              <Exec>number++</Exec>\r\n            </Loop>\r\n          </CustomListItems>\r\n        </Expand>\r\n      </Synthetic>\r\n      \r\n      <CustomListItems>\r\n        <Variable Name=\"child_this\" InitialValue=\"&amp;first_child\" />\r\n        <Variable Name=\"child_data\" InitialValue=\"first_child._data\" />\r\n        <Variable Name=\"child_data_copy\" InitialValue=\"child_data\" />\r\n\r\n        <!-- first_child struct template arguments -->\r\n        <Variable Name=\"child_T1\" InitialValue=\"8\" />\r\n        <Variable Name=\"child_T2\" InitialValue=\"0\" />\r\n\r\n        <Variable Name=\"compact_alignment_log2\" InitialValue=\"2\" />\r\n        <Variable Name=\"compact_alignment\" InitialValue=\"1 &lt;&lt; compact_alignment_log2\" />\r\n\r\n        <!-- compact_get_page() -->\r\n        <Variable Name=\"_page\" InitialValue=\"*(char*)(child_this - child_T1)\" />\r\n        <Variable Name=\"page\" InitialValue=\"((child_this - child_T1 - (_page &lt;&lt; compact_alignment_log2)) - *(unsigned*)(child_this - child_T1 - (_page &lt;&lt; compact_alignment_log2)))\" />\r\n\r\n        <!-- page->allocator->_hash -->\r\n        <Variable Name=\"allocator\" InitialValue=\"*(size_t*)page\" />\r\n        <Variable Name=\"_hash\" InitialValue=\"*(size_t*)(allocator + 2 * sizeof(size_t))\" /><!--2 pointer offsetof(allocator, _hash)-->\r\n        <Variable Name=\"_items\" InitialValue=\"*(size_t*)_hash\" />\r\n        <Variable Name=\"_capacity\" InitialValue=\"*((size_t*)_hash + 1)\" /><!--1 pointer offsetof(_hash, _capacity)-->\r\n        <Variable Name=\"_count\" InitialValue=\"*((size_t*)_hash + 2)\" /><!--2 pointer offsetof(_hash, _count)-->\r\n\r\n        <!-- find() prolog -->\r\n        <Variable Name=\"hashmod\" InitialValue=\"_capacity - 1\" />\r\n\r\n        <Variable Name=\"h\" InitialValue=\"(unsigned)child_this\" />\r\n        <Variable Name=\"bucket\" InitialValue=\"0\" />\r\n\r\n        <Variable Name=\"probe\" InitialValue=\"0\" />\r\n        <Variable Name=\"probe_item\" InitialValue=\"(size_t*)0\" />\r\n\r\n        <Variable Name=\"child_real\" InitialValue=\"(pugi::xml_node_struct*)0\" />\r\n\r\n        <!-- if _data < 255 -->\r\n        <Variable Name=\"child_short\" InitialValue=\"(pugi::xml_node_struct*)(((size_t)child_this &amp; ~(compact_alignment - 1)) + (child_data - 1 + child_T2) * compact_alignment)\" />\r\n\r\n        <Variable Name=\"number\" InitialValue=\"0\" />\r\n\r\n        <Loop Condition=\"child_this &amp;&amp; child_data\">\r\n          <!-- find() hash -->\r\n          <Exec>h = h ^ (h >> 16)</Exec>\r\n          <Exec>h = h * (0x85ebca6bu)</Exec>\r\n          <Exec>h = h ^ (h >> 13)</Exec>\r\n          <Exec>h = h * (0xc2b2ae35u)</Exec>\r\n          <Exec>h = h ^ (h >> 16)</Exec>\r\n\r\n          <Exec>bucket = h &amp; hashmod</Exec>\r\n\r\n          <!-- find() loop -->\r\n          <Loop Condition=\"probe &lt;= hashmod &amp;&amp;_capacity\">\r\n            <Exec>probe_item = (size_t*)_items + bucket * 2</Exec><!--2 pointer sizeof(item_t)-->\r\n\r\n            <If Condition=\"*probe_item == child_this || *probe_item == 0\">\r\n              <Exec>child_real = *(pugi::xml_node_struct**)(probe_item + 1)</Exec><!--1 pointer offsetof(item_t, value)-->\r\n              <Break/>\r\n            </If>\r\n\r\n            <Exec>bucket = (bucket + probe + 1) &amp; hashmod</Exec>\r\n            <Exec>probe++</Exec>\r\n          </Loop>\r\n\r\n          <Exec>child_data_copy = child_data</Exec>\r\n\r\n          <If Condition=\"child_data_copy &gt;= 255 &amp;&amp; child_real\">\r\n            <Item Name=\"[{number}]\">*child_real,view(child)</Item>\r\n            <Exec>child_this = (size_t)&amp;(*child_real).next_sibling</Exec>\r\n            <Exec>child_data = (*child_real).next_sibling._data</Exec>\r\n          </If>\r\n          <If Condition=\"child_data_copy &lt; 255 &amp;&amp; child_short\">\r\n            <Item Name=\"[{number}]\">*child_short,view(child)</Item>\r\n            <Exec>child_this = (size_t)&amp;(*child_short).next_sibling</Exec>\r\n            <Exec>child_data = (*child_short).next_sibling._data</Exec>\r\n          </If>\r\n\r\n          <!-- next_sibling struct template arguments -->\r\n          <Exec>child_T1 = 10</Exec>\r\n          <Exec>child_T2 = 0</Exec>\r\n\r\n          <!-- find() prolog again -->\r\n          <Exec>h = (unsigned)child_this</Exec>\r\n          <Exec>bucket = 0</Exec>\r\n\r\n          <Exec>probe = 0</Exec>\r\n          <Exec>probe_item = (size_t*)0</Exec>\r\n\r\n          <Exec>child_real = (pugi::xml_node_struct*)0</Exec>\r\n          <Exec>child_short = (pugi::xml_node_struct*)(((size_t)child_this &amp; ~(compact_alignment - 1)) + (child_data - 1 + child_T2) * compact_alignment)</Exec>\r\n\r\n          <Exec>number++</Exec>\r\n        </Loop>\r\n      </CustomListItems>\r\n      \r\n      <Item Name=\"next_sibling\" ExcludeView=\"child\">next_sibling</Item>\r\n    </Expand>\r\n  </Type>\r\n  \r\n  <Type Name=\"pugi::xml_attribute_struct\">\r\n    <Expand>\r\n      <Item Name=\"name\">name,na</Item>\r\n      <Item Name=\"value\">value,na</Item>\r\n      \r\n      <CustomListItems ExcludeView=\"child\">\r\n        <Variable Name=\"attribute_this\" InitialValue=\"&amp;next_attribute\" />\r\n        <Variable Name=\"attribute_data\" InitialValue=\"next_attribute._data\" />\r\n\r\n        <!-- next_attribute struct template arguments -->\r\n        <Variable Name=\"attribute_T1\" InitialValue=\"7\" />\r\n        <Variable Name=\"attribute_T2\" InitialValue=\"0\" />\r\n\r\n        <Variable Name=\"compact_alignment_log2\" InitialValue=\"2\" />\r\n        <Variable Name=\"compact_alignment\" InitialValue=\"1 &lt;&lt; compact_alignment_log2\" />\r\n\r\n        <!-- compact_get_page() -->\r\n        <Variable Name=\"_page\" InitialValue=\"*(char*)(attribute_this - attribute_T1)\" />\r\n        <Variable Name=\"page\" InitialValue=\"((attribute_this - attribute_T1 - (_page &lt;&lt; compact_alignment_log2)) - *(unsigned*)(attribute_this - attribute_T1 - (_page &lt;&lt; compact_alignment_log2)))\" />\r\n\r\n        <!-- page->allocator->_hash -->\r\n        <Variable Name=\"allocator\" InitialValue=\"*(size_t*)page\" />\r\n        <Variable Name=\"_hash\" InitialValue=\"*(size_t*)(allocator + 2 * sizeof(size_t))\" /><!--2 pointer offsetof(allocator, _hash)-->\r\n        <Variable Name=\"_items\" InitialValue=\"*(size_t*)_hash\" />\r\n        <Variable Name=\"_capacity\" InitialValue=\"*((size_t*)_hash + 1)\" /><!--1 pointer offsetof(_hash, _capacity)-->\r\n        <Variable Name=\"_count\" InitialValue=\"*((size_t*)_hash + 2)\" /><!--2 pointer offsetof(_hash, _count)-->\r\n\r\n        <!-- find() prolog -->\r\n        <Variable Name=\"hashmod\" InitialValue=\"_capacity - 1\" />\r\n\r\n        <Variable Name=\"h\" InitialValue=\"(unsigned)attribute_this\" />\r\n        <Variable Name=\"bucket\" InitialValue=\"0\" />\r\n\r\n        <Variable Name=\"probe\" InitialValue=\"0\" />\r\n        <Variable Name=\"probe_item\" InitialValue=\"(size_t*)0\" />\r\n\r\n        <Variable Name=\"attribute_real\" InitialValue=\"(pugi::xml_attribute_struct*)0\" />\r\n\r\n        <!-- if _data < 255 -->\r\n        <Variable Name=\"attribute_short\" InitialValue=\"(pugi::xml_attribute_struct*)(((size_t)attribute_this &amp; ~(compact_alignment - 1)) + (attribute_data - 1 + attribute_T2) * compact_alignment)\" />\r\n\r\n        <!-- find() hash -->\r\n        <Exec>h = h ^ (h >> 16)</Exec>\r\n        <Exec>h = h * (0x85ebca6bu)</Exec>\r\n        <Exec>h = h ^ (h >> 13)</Exec>\r\n        <Exec>h = h * (0xc2b2ae35u)</Exec>\r\n        <Exec>h = h ^ (h >> 16)</Exec>\r\n\r\n        <Exec>bucket = h &amp; hashmod</Exec>\r\n\r\n        <!-- find() loop -->\r\n        <Loop Condition=\"probe &lt;= hashmod &amp;&amp;_capacity\">\r\n          <Exec>probe_item = (size_t*)_items + bucket * 2</Exec><!--2 pointer sizeof(item_t)-->\r\n\r\n          <If Condition=\"*probe_item == attribute_this || *probe_item == 0\">\r\n            <Exec>attribute_real = *(pugi::xml_attribute_struct**)(probe_item + 1)</Exec><!--1 pointer offsetof(item_t, value)-->\r\n            <Break/>\r\n          </If>\r\n\r\n          <Exec>bucket = (bucket + probe + 1) &amp; hashmod</Exec>\r\n          <Exec>probe++</Exec>\r\n        </Loop>\r\n\r\n        <If Condition=\"attribute_data &gt;= 255 &amp;&amp; attribute_real\">\r\n          <Item Name=\"next_attribute\">*attribute_real</Item>\r\n        </If>\r\n        <If Condition=\"attribute_data != 0 &amp;&amp; attribute_data &lt; 255 &amp;&amp; attribute_short\">\r\n          <Item Name=\"next_attribute\">*attribute_short</Item>\r\n        </If>\r\n      </CustomListItems>\r\n    </Expand>\r\n  </Type>\r\n\r\n  <Type Name=\"pugi::impl::`anonymous-namespace'::compact_string&lt;*,*&gt;\">\r\n    <Expand HideRawView=\"1\">\r\n      <CustomListItems Condition=\"_data &amp;&amp; _data &lt; 255\">\r\n        <Variable Name=\"compact_alignment_log2\" InitialValue=\"2\" />\r\n        <Variable Name=\"compact_alignment\" InitialValue=\"1 &lt;&lt; compact_alignment_log2\" />\r\n        \r\n        <!-- compact_get_page() -->\r\n        <Variable Name=\"_page\" InitialValue=\"*(char*)(this - $T1)\" />\r\n        <Variable Name=\"page\" InitialValue=\"((this - $T1 - (_page &lt;&lt; compact_alignment_log2)) - *(unsigned*)(this - $T1 - (_page &lt;&lt; compact_alignment_log2)))\" />\r\n\r\n        <Variable Name=\"compact_string_base\" InitialValue=\"*(size_t*)(page + 5 * sizeof(void*))\" /><!-- 5 pointer offsetof(page, compact_string_base)-->\r\n        <Variable Name=\"base\" InitialValue=\"this - $T2\" />\r\n        <Variable Name=\"offset\" InitialValue=\"((*(short*)base - 1) &lt;&lt; 7) + (_data - 1)\" />\r\n        \r\n        <Item Name=\"value\">(pugi::char_t*)(compact_string_base + offset * sizeof(pugi::char_t)),na</Item>\r\n      </CustomListItems>\r\n      \r\n      <CustomListItems Condition=\"_data &amp;&amp; _data &gt;= 255\">\r\n        <Variable Name=\"compact_alignment_log2\" InitialValue=\"2\" />\r\n        <Variable Name=\"compact_alignment\" InitialValue=\"1 &lt;&lt; compact_alignment_log2\" />\r\n        \r\n        <!-- compact_get_page() -->\r\n        <Variable Name=\"_page\" InitialValue=\"*(char*)(this - $T1)\" />\r\n        <Variable Name=\"page\" InitialValue=\"((this - $T1 - (_page &lt;&lt; compact_alignment_log2)) - *(unsigned*)(this - $T1 - (_page &lt;&lt; compact_alignment_log2)))\" />\r\n\r\n        <!-- page->allocator->_hash -->\r\n        <Variable Name=\"allocator\" InitialValue=\"*(size_t*)page\" />\r\n        <Variable Name=\"_hash\" InitialValue=\"*(size_t*)(allocator + 2 * sizeof(size_t))\" /><!--2 pointer offsetof(allocator, _hash)-->\r\n        <Variable Name=\"_items\" InitialValue=\"*(size_t*)_hash\" />\r\n        <Variable Name=\"_capacity\" InitialValue=\"*((size_t*)_hash + 1)\" /><!--1 pointer offsetof(_hash, _capacity)-->\r\n        <Variable Name=\"_count\" InitialValue=\"*((size_t*)_hash + 2)\" /><!--2 pointer offsetof(_hash, _count)-->\r\n\r\n        <!-- find() prolog -->\r\n        <Variable Name=\"hashmod\" InitialValue=\"_capacity - 1\" />\r\n\r\n        <Variable Name=\"h\" InitialValue=\"(unsigned)this\" />\r\n        <Variable Name=\"bucket\" InitialValue=\"0\" />\r\n\r\n        <Variable Name=\"probe\" InitialValue=\"0\" />\r\n        <Variable Name=\"probe_item\" InitialValue=\"(size_t*)0\" />\r\n\r\n        <!-- find() hash -->\r\n        <Exec>h = h ^ (h >> 16)</Exec>\r\n        <Exec>h = h * (0x85ebca6bu)</Exec>\r\n        <Exec>h = h ^ (h >> 13)</Exec>\r\n        <Exec>h = h * (0xc2b2ae35u)</Exec>\r\n        <Exec>h = h ^ (h >> 16)</Exec>\r\n\r\n        <Exec>bucket = h &amp; hashmod</Exec>\r\n\r\n        <!-- find() loop -->\r\n        <Loop Condition=\"probe &lt;= hashmod &amp;&amp;_capacity\">\r\n          <Exec>probe_item = (size_t*)_items + bucket * 2</Exec><!--2 pointer sizeof(item_t)-->\r\n\r\n          <If Condition=\"*probe_item == this || *probe_item == 0\">\r\n            <Item Name=\"value\">*(pugi::char_t**)(probe_item + 1)</Item><!--1 pointer offsetof(item_t, value)-->\r\n            <Break/>\r\n          </If>\r\n\r\n          <Exec>bucket = (bucket + probe + 1) &amp; hashmod</Exec>\r\n          <Exec>probe++</Exec>\r\n        </Loop>\r\n      </CustomListItems>\r\n    </Expand>\r\n  </Type>\r\n  \r\n  <Type Name=\"pugi::impl::`anonymous-namespace'::compact_pointer&lt;pugi::xml_node_struct,*,*&gt;\">\r\n    <DisplayString Condition=\"!_data\">nullptr</DisplayString>\r\n    <DisplayString Condition=\"_data\">...</DisplayString>\r\n    \r\n    <Expand HideRawView=\"1\">\r\n      <CustomListItems Condition=\"_data &amp;&amp; _data &lt; 255\">\r\n        <Variable Name=\"compact_alignment_log2\" InitialValue=\"2\" />\r\n        <Variable Name=\"compact_alignment\" InitialValue=\"1 &lt;&lt; compact_alignment_log2\" />\r\n        \r\n        <Item Name=\"value\">*(pugi::xml_node_struct*)(((size_t)this &amp; ~(compact_alignment - 1)) + (_data - 1 + $T2) * compact_alignment)</Item>\r\n      </CustomListItems>\r\n      \r\n      <CustomListItems Condition=\"_data &amp;&amp; _data &gt;= 255\">\r\n        <Variable Name=\"compact_alignment_log2\" InitialValue=\"2\" />\r\n        <Variable Name=\"compact_alignment\" InitialValue=\"1 &lt;&lt; compact_alignment_log2\" />\r\n        \r\n        <!-- compact_get_page() -->\r\n        <Variable Name=\"_page\" InitialValue=\"*(char*)(this - $T1)\" />\r\n        <Variable Name=\"page\" InitialValue=\"((this - $T1 - (_page &lt;&lt; compact_alignment_log2)) - *(unsigned*)(this - $T1 - (_page &lt;&lt; compact_alignment_log2)))\" />\r\n\r\n        <!-- page->allocator->_hash -->\r\n        <Variable Name=\"allocator\" InitialValue=\"*(size_t*)page\" />\r\n        <Variable Name=\"_hash\" InitialValue=\"*(size_t*)(allocator + 2 * sizeof(size_t))\" /><!--2 pointer offsetof(allocator, _hash)-->\r\n        <Variable Name=\"_items\" InitialValue=\"*(size_t*)_hash\" />\r\n        <Variable Name=\"_capacity\" InitialValue=\"*((size_t*)_hash + 1)\" /><!--1 pointer offsetof(_hash, _capacity)-->\r\n        <Variable Name=\"_count\" InitialValue=\"*((size_t*)_hash + 2)\" /><!--2 pointer offsetof(_hash, _count)-->\r\n\r\n        <!-- find() prolog -->\r\n        <Variable Name=\"hashmod\" InitialValue=\"_capacity - 1\" />\r\n\r\n        <Variable Name=\"h\" InitialValue=\"(unsigned)this\" />\r\n        <Variable Name=\"bucket\" InitialValue=\"0\" />\r\n\r\n        <Variable Name=\"probe\" InitialValue=\"0\" />\r\n        <Variable Name=\"probe_item\" InitialValue=\"(size_t*)0\" />\r\n\r\n        <!-- find() hash -->\r\n        <Exec>h = h ^ (h >> 16)</Exec>\r\n        <Exec>h = h * (0x85ebca6bu)</Exec>\r\n        <Exec>h = h ^ (h >> 13)</Exec>\r\n        <Exec>h = h * (0xc2b2ae35u)</Exec>\r\n        <Exec>h = h ^ (h >> 16)</Exec>\r\n\r\n        <Exec>bucket = h &amp; hashmod</Exec>\r\n\r\n        <!-- find() loop -->\r\n        <Loop Condition=\"probe &lt;= hashmod &amp;&amp;_capacity\">\r\n          <Exec>probe_item = (size_t*)_items + bucket * 2</Exec><!--2 pointer sizeof(item_t)-->\r\n\r\n          <If Condition=\"*probe_item == this || *probe_item == 0\">\r\n            <Item Name=\"value\">**(pugi::xml_node_struct**)(probe_item + 1)</Item><!--1 pointer offsetof(item_t, value)-->\r\n            <Break/>\r\n          </If>\r\n\r\n          <Exec>bucket = (bucket + probe + 1) &amp; hashmod</Exec>\r\n          <Exec>probe++</Exec>\r\n        </Loop>\r\n      </CustomListItems>\r\n    </Expand>\r\n  </Type>\r\n\r\n  <Type Name=\"pugi::impl::`anonymous-namespace'::compact_pointer&lt;pugi::xml_attribute_struct,*,*&gt;\">\r\n    <DisplayString Condition=\"!_data\">nullptr</DisplayString>\r\n    <DisplayString Condition=\"_data\">...</DisplayString>\r\n    \r\n    <Expand HideRawView=\"1\">\r\n      <CustomListItems Condition=\"_data &amp;&amp; _data &lt; 255\">\r\n        <Variable Name=\"compact_alignment_log2\" InitialValue=\"2\" />\r\n        <Variable Name=\"compact_alignment\" InitialValue=\"1 &lt;&lt; compact_alignment_log2\" />\r\n        \r\n        <Item Name=\"value\">*(pugi::xml_attribute_struct*)(((size_t)this &amp; ~(compact_alignment - 1)) + (_data - 1 + $T2) * compact_alignment)</Item>\r\n      </CustomListItems>\r\n      \r\n      <CustomListItems Condition=\"_data &amp;&amp; _data &gt;= 255\">\r\n        <Variable Name=\"compact_alignment_log2\" InitialValue=\"2\" />\r\n        <Variable Name=\"compact_alignment\" InitialValue=\"1 &lt;&lt; compact_alignment_log2\" />\r\n        \r\n        <!-- compact_get_page() -->\r\n        <Variable Name=\"_page\" InitialValue=\"*(char*)(this - $T1)\" />\r\n        <Variable Name=\"page\" InitialValue=\"((this - $T1 - (_page &lt;&lt; compact_alignment_log2)) - *(unsigned*)(this - $T1 - (_page &lt;&lt; compact_alignment_log2)))\" />\r\n\r\n        <!-- page->allocator->_hash -->\r\n        <Variable Name=\"allocator\" InitialValue=\"*(size_t*)page\" />\r\n        <Variable Name=\"_hash\" InitialValue=\"*(size_t*)(allocator + 2 * sizeof(size_t))\" /><!--2 pointer offsetof(allocator, _hash)-->\r\n        <Variable Name=\"_items\" InitialValue=\"*(size_t*)_hash\" />\r\n        <Variable Name=\"_capacity\" InitialValue=\"*((size_t*)_hash + 1)\" /><!--1 pointer offsetof(_hash, _capacity)-->\r\n        <Variable Name=\"_count\" InitialValue=\"*((size_t*)_hash + 2)\" /><!--2 pointer offsetof(_hash, _count)-->\r\n\r\n        <!-- find() prolog -->\r\n        <Variable Name=\"hashmod\" InitialValue=\"_capacity - 1\" />\r\n\r\n        <Variable Name=\"h\" InitialValue=\"(unsigned)this\" />\r\n        <Variable Name=\"bucket\" InitialValue=\"0\" />\r\n\r\n        <Variable Name=\"probe\" InitialValue=\"0\" />\r\n        <Variable Name=\"probe_item\" InitialValue=\"(size_t*)0\" />\r\n\r\n        <!-- find() hash -->\r\n        <Exec>h = h ^ (h >> 16)</Exec>\r\n        <Exec>h = h * (0x85ebca6bu)</Exec>\r\n        <Exec>h = h ^ (h >> 13)</Exec>\r\n        <Exec>h = h * (0xc2b2ae35u)</Exec>\r\n        <Exec>h = h ^ (h >> 16)</Exec>\r\n\r\n        <Exec>bucket = h &amp; hashmod</Exec>\r\n\r\n        <!-- find() loop -->\r\n        <Loop Condition=\"probe &lt;= hashmod &amp;&amp;_capacity\">\r\n          <Exec>probe_item = (size_t*)_items + bucket * 2</Exec><!--2 pointer sizeof(item_t)-->\r\n\r\n          <If Condition=\"*probe_item == this || *probe_item == 0\">\r\n            <Item Name=\"value\">**(pugi::xml_attribute_struct**)(probe_item + 1)</Item><!--1 pointer offsetof(item_t, value)-->\r\n            <Break/>\r\n          </If>\r\n\r\n          <Exec>bucket = (bucket + probe + 1) &amp; hashmod</Exec>\r\n          <Exec>probe++</Exec>\r\n        </Loop>\r\n      </CustomListItems>\r\n    </Expand>\r\n  </Type>\r\n\r\n  <Type Name=\"pugi::xpath_node\">\r\n    <DisplayString Condition=\"_node._root &amp;&amp; _attribute._attr\">{_node,na} {_attribute,na}</DisplayString>\r\n    <DisplayString Condition=\"_node._root\">{_node,na}</DisplayString>\r\n    <DisplayString Condition=\"_attribute._attr\">{_attribute}</DisplayString>\r\n    <DisplayString>empty</DisplayString>\r\n    \r\n    <Expand HideRawView=\"1\">\r\n      <ExpandedItem Condition=\"_node._root &amp;&amp; !_attribute._attr\">_node</ExpandedItem>\r\n      <ExpandedItem Condition=\"!_node._root &amp;&amp; _attribute._attr\">_attribute</ExpandedItem>\r\n      \r\n      <Item Name=\"node\" Condition=\"_node._root &amp;&amp; _attribute._attr\">_node,na</Item>\r\n      <Item Name=\"attribute\" Condition=\"_node._root &amp;&amp; _attribute._attr\">_attribute,na</Item>\r\n    </Expand>\r\n  </Type>\r\n  \r\n  <Type Name=\"pugi::xpath_node_set\">\r\n    <Expand>\r\n      <Item Name=\"type\">_type</Item>\r\n      <ArrayItems>\r\n        <Size>_end - _begin</Size>\r\n        <ValuePointer>_begin</ValuePointer>\r\n      </ArrayItems>\r\n    </Expand>\r\n  </Type>\r\n</AutoVisualizer>"
  },
  {
    "path": "third-party/pugixml/scripts/nuget/pugixml.nuspec",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<package xmlns=\"http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd\">\r\n  <metadata>\r\n    <id>pugixml</id>\r\n    <version>1.14.0-appveyor</version>\r\n    <title>pugixml</title>\r\n    <authors>Arseny Kapoulkine</authors>\r\n    <owners>Arseny Kapoulkine</owners>\r\n    <requireLicenseAcceptance>false</requireLicenseAcceptance>\r\n    <license type=\"expression\">MIT</license>\r\n    <projectUrl>https://pugixml.org/</projectUrl>\r\n    <description>pugixml is a C++ XML processing library, which consists of a DOM-like interface with rich traversal/modification capabilities, an extremely fast XML parser which constructs the DOM tree from an XML file/buffer, and an XPath 1.0 implementation for complex data-driven tree queries. Full Unicode support is also available, with Unicode interface variants and conversions between different Unicode encodings (which happen automatically during parsing/saving).\r\npugixml is used by a lot of projects, both open-source and proprietary, for performance and easy-to-use interface.\r\nThis package contains builds for VS2013, VS2015, VS2017, VS2019 and VS2022, for both statically linked and DLL CRT; you can switch the CRT linkage in Project -> Properties -> Referenced Packages -> pugixml.</description>\r\n    <summary>Light-weight, simple and fast XML parser for C++ with XPath support</summary>\r\n    <releaseNotes>https://pugixml.org/docs/manual.html#changes</releaseNotes>\r\n    <copyright>Copyright (c) 2006-2023 Arseny Kapoulkine</copyright>\r\n    <tags>native nativepackage</tags>\r\n  </metadata>\r\n</package>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/nuget_build.ps1",
    "content": "function Run-Command([string]$cmd)\r\n{\r\n\tInvoke-Expression $cmd\r\n\tif ($LastExitCode) { exit $LastExitCode }\r\n}\r\n\r\nfunction Force-Copy([string]$from, [string]$to)\r\n{\r\n\tWrite-Host $from \"->\" $to\r\n\tNew-Item -Force $to | Out-Null\r\n\tCopy-Item -Force $from $to\r\n\tif (! $?) { exit 1 }\r\n}\r\n\r\nfunction Build-Version([string]$vs, [string]$toolset, [string]$linkage)\r\n{\r\n\t$prjsuffix = if ($linkage -eq \"static\") { \"_static\" } else { \"\" }\r\n\t$cfgsuffix = if ($linkage -eq \"static\") { \"Static\" } else { \"\" }\r\n\r\n\tforeach ($configuration in \"Debug\",\"Release\")\r\n\t{\r\n\t\tRun-Command \"msbuild pugixml_$vs$prjsuffix.vcxproj /t:Rebuild /p:Configuration=$configuration /p:Platform=x86 /v:minimal /nologo\"\r\n\t\tRun-Command \"msbuild pugixml_$vs$prjsuffix.vcxproj /t:Rebuild /p:Configuration=$configuration /p:Platform=x64 /v:minimal /nologo\"\r\n\r\n\t\tForce-Copy \"$vs/Win32_$configuration$cfgsuffix/pugixml.lib\" \"nuget/build/native/lib/Win32/$toolset/$linkage/$configuration/pugixml.lib\"\r\n\t\tForce-Copy \"$vs/x64_$configuration$cfgsuffix/pugixml.lib\" \"nuget/build/native/lib/x64/$toolset/$linkage/$configuration/pugixml.lib\"\r\n\t}\r\n}\r\n\r\nPush-Location\r\n$scriptdir = Split-Path $MyInvocation.MyCommand.Path\r\ncd $scriptdir\r\n\r\nForce-Copy \"../src/pugiconfig.hpp\" \"nuget/build/native/include/pugiconfig.hpp\"\r\nForce-Copy \"../src/pugixml.hpp\" \"nuget/build/native/include/pugixml.hpp\"\r\nForce-Copy \"../src/pugixml.cpp\" \"nuget/build/native/include/pugixml.cpp\"\r\n\r\nif ($args[0] -eq 2022){\r\n\tBuild-Version \"vs2022\" \"v143\" \"dynamic\"\r\n\tBuild-Version \"vs2022\" \"v143\" \"static\"\r\n\r\n} elseif ($args[0] -eq 2019){\r\n\tBuild-Version \"vs2019\" \"v142\" \"dynamic\"\r\n\tBuild-Version \"vs2019\" \"v142\" \"static\"\r\n\r\n} elseif ($args[0] -eq 2017){\r\n\tBuild-Version \"vs2017\" \"v141\" \"dynamic\"\r\n\tBuild-Version \"vs2017\" \"v141\" \"static\"\r\n\r\n\tBuild-Version \"vs2015\" \"v140\" \"dynamic\"\r\n\tBuild-Version \"vs2015\" \"v140\" \"static\"\r\n\r\n\tBuild-Version \"vs2013\" \"v120\" \"dynamic\"\r\n\tBuild-Version \"vs2013\" \"v120\" \"static\"\r\n\r\n} elseif($args[0] -eq 2015){\r\n\tBuild-Version \"vs2015\" \"v140\" \"dynamic\"\r\n\tBuild-Version \"vs2015\" \"v140\" \"static\"\r\n\r\n\tBuild-Version \"vs2013\" \"v120\" \"dynamic\"\r\n\tBuild-Version \"vs2013\" \"v120\" \"static\"\r\n\r\n} elseif($args[0] -eq 2013){\r\n\tBuild-Version \"vs2013\" \"v120\" \"dynamic\"\r\n\tBuild-Version \"vs2013\" \"v120\" \"static\"\r\n}\r\n\r\nRun-Command \"nuget pack nuget\"\r\n\r\nPop-Location\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/premake4.lua",
    "content": "-- Reset RNG seed to get consistent results across runs (i.e. XCode)\r\nmath.randomseed(12345)\r\n\r\nlocal static = _ARGS[1] == 'static'\r\nlocal action = premake.action.current()\r\n\r\nif string.startswith(_ACTION, \"vs\") then\r\n\tif action then\r\n\t\t-- Disable solution generation\r\n\t\tfunction action.onsolution(sln)\r\n\t\t\tsln.vstudio_configs = premake.vstudio_buildconfigs(sln)\r\n\t\tend\r\n\r\n\t\t-- Rename output file\r\n\t\tfunction action.onproject(prj)\r\n            local name = \"%%_\" .. _ACTION .. (static and \"_static\" or \"\")\r\n\r\n            if static then\r\n                for k, v in pairs(prj.project.__configs) do\r\n                    v.objectsdir = v.objectsdir .. \"Static\"\r\n                end\r\n            end\r\n\r\n            if _ACTION == \"vs2010\" then\r\n                premake.generate(prj, name .. \".vcxproj\", premake.vs2010_vcxproj)\r\n            else\r\n                premake.generate(prj, name .. \".vcproj\", premake.vs200x_vcproj)\r\n            end\r\n\t\tend\r\n\tend\r\nelseif _ACTION == \"codeblocks\" then\r\n\taction.onsolution = nil\r\n\r\n\tfunction action.onproject(prj)\r\n\t\tpremake.generate(prj, \"%%_\" .. _ACTION .. \".cbp\", premake.codeblocks_cbp)\r\n\tend\r\nelseif _ACTION == \"codelite\" then\r\n\taction.onsolution = nil\r\n\r\n\tfunction action.onproject(prj)\r\n\t\tpremake.generate(prj, \"%%_\" .. _ACTION .. \".project\", premake.codelite_project)\r\n\tend\r\nend\r\n\r\nsolution \"pugixml\"\r\n\tobjdir(_ACTION)\r\n\ttargetdir(_ACTION)\r\n\r\nif string.startswith(_ACTION, \"vs\") then\r\n\tif _ACTION ~= \"vs2002\" and _ACTION ~= \"vs2003\" then\r\n\t\tplatforms { \"x32\", \"x64\" }\r\n\r\n\t\tconfiguration \"x32\" targetdir(_ACTION .. \"/x32\")\r\n\t\tconfiguration \"x64\" targetdir(_ACTION .. \"/x64\")\r\n\tend\r\n\r\n\tconfigurations { \"Debug\", \"Release\" }\r\n\r\n    if static then\r\n        configuration \"Debug\" targetsuffix \"sd\"\r\n        configuration \"Release\" targetsuffix \"s\"\r\n    else\r\n        configuration \"Debug\" targetsuffix \"d\"\r\n    end\r\nelse\r\n\tif _ACTION == \"xcode3\" then\r\n\t\tplatforms \"universal\"\r\n\tend\r\n\r\n\tconfigurations { \"Debug\", \"Release\" }\r\n\r\n\tconfiguration \"Debug\" targetsuffix \"d\"\r\nend\r\n\r\nproject \"pugixml\"\r\n\tkind \"StaticLib\"\r\n\tlanguage \"C++\"\r\n\tfiles { \"../src/pugixml.hpp\", \"../src/pugiconfig.hpp\", \"../src/pugixml.cpp\" }\r\n\tflags { \"NoPCH\", \"NoMinimalRebuild\", \"NoEditAndContinue\", \"Symbols\" }\r\n\tuuid \"89A1E353-E2DC-495C-B403-742BE206ACED\"\r\n\r\nconfiguration \"Debug\"\r\n\tdefines { \"_DEBUG\" }\r\n\r\nconfiguration \"Release\"\r\n\tdefines { \"NDEBUG\" }\r\n\tflags { \"Optimize\" }\r\n\r\nif static then\r\n    configuration \"*\"\r\n        flags { \"StaticRuntime\" }\r\nend\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml-config.cmake.in",
    "content": "@PACKAGE_INIT@\r\n\r\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/pugixml-targets.cmake\")\r\n\r\n# If the user is not requiring 1.11 (either by explicitly requesting an older\r\n# version or not requesting one at all), provide the old imported target name\r\n# for compatibility.\r\nif (NOT TARGET pugixml AND (NOT DEFINED PACKAGE_FIND_VERSION OR PACKAGE_FIND_VERSION VERSION_LESS \"1.11\"))\r\n  add_library(pugixml INTERFACE IMPORTED)\r\n  # Equivalent to target_link_libraries INTERFACE, but compatible with CMake 3.10\r\n  set_target_properties(pugixml PROPERTIES INTERFACE_LINK_LIBRARIES pugixml::pugixml)\r\nendif ()\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\r\nexec_prefix=${prefix}\r\nincludedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@@INSTALL_SUFFIX@\r\nlibdir=@CMAKE_INSTALL_FULL_LIBDIR@@INSTALL_SUFFIX@\r\n\r\nName: pugixml\r\nDescription: Light-weight, simple and fast XML parser for C++ with XPath support.\r\nURL: https://pugixml.org/\r\nVersion: @pugixml_VERSION@\r\nCflags: -I${includedir}\r\nLibs: -L${libdir} -lpugixml@LIB_POSTFIX@\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml.podspec",
    "content": "Pod::Spec.new do |s|\r\n  s.name         = \"pugixml\"\r\n  s.version      = \"1.14\"\r\n  s.summary      = \"C++ XML parser library.\"\r\n  s.homepage     = \"https://pugixml.org\"\r\n  s.license      = \"MIT\"\r\n  s.author       = { \"Arseny Kapoulkine\" => \"arseny.kapoulkine@gmail.com\" }\r\n  s.platform     = :ios, \"7.0\"\r\n  \r\n  s.source = { :git => \"https://github.com/zeux/pugixml.git\", :tag => \"v\" + s.version.to_s }\r\n\r\n  s.source_files  = \"src/**/*.{hpp,cpp}\"\r\n  s.header_mappings_dir = \"src\"\r\nend\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\r\n{\r\n\tarchiveVersion = 1;\r\n\tclasses = {\r\n\t};\r\n\tobjectVersion = 45;\r\n\tobjects = {\r\n\r\n/* Begin PBXBuildFile section */\r\n\t\t0424128F67AB5C730232235E /* pugixml.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 47481C4F0E03673E0E780637 /* pugixml.cpp */; };\r\n/* End PBXBuildFile section */\r\n\r\n/* Begin PBXFileReference section */\r\n\t\t0B66463C5F896E6449051D38 /* pugiconfig.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = \"pugiconfig.hpp\"; path = \"pugiconfig.hpp\"; sourceTree = \"<group>\"; };\r\n\t\t47481C4F0E03673E0E780637 /* pugixml.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = \"pugixml.cpp\"; path = \"pugixml.cpp\"; sourceTree = \"<group>\"; };\r\n\t\t6C911F0460FC44CD3B1B5624 /* pugixml.hpp */ = {isa = PBXFileReference; lastKnownFileType = text; name = \"pugixml.hpp\"; path = \"pugixml.hpp\"; sourceTree = \"<group>\"; };\r\n\t\t1DA04ADC64C3566D16C45B6D /* libpugixmld.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = \"libpugixmld.a\"; path = \"libpugixmld.a\"; sourceTree = BUILT_PRODUCTS_DIR; };\r\n/* End PBXFileReference section */\r\n\r\n/* Begin PBXFrameworksBuildPhase section */\r\n\t\t2BA00212518037166623673F /* Frameworks */ = {\r\n\t\t\tisa = PBXFrameworksBuildPhase;\r\n\t\t\tbuildActionMask = 2147483647;\r\n\t\t\tfiles = (\r\n\t\t\t);\r\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\r\n\t\t};\r\n/* End PBXFrameworksBuildPhase section */\r\n\r\n/* Begin PBXGroup section */\r\n\t\t19E0517F3CF26ED63AE23641 /* pugixml */ = {\r\n\t\t\tisa = PBXGroup;\r\n\t\t\tchildren = (\r\n\t\t\t\t578963B4309E714F05E01D71 /* src */,\r\n\t\t\t\t219F66186DDF392149043810 /* Products */,\r\n\t\t\t);\r\n\t\t\tname = \"pugixml\";\r\n\t\t\tsourceTree = \"<group>\";\r\n\t\t};\r\n\t\t578963B4309E714F05E01D71 /* src */ = {\r\n\t\t\tisa = PBXGroup;\r\n\t\t\tchildren = (\r\n\t\t\t\t0B66463C5F896E6449051D38 /* pugiconfig.hpp */,\r\n\t\t\t\t47481C4F0E03673E0E780637 /* pugixml.cpp */,\r\n\t\t\t\t6C911F0460FC44CD3B1B5624 /* pugixml.hpp */,\r\n\t\t\t);\r\n\t\t\tname = \"src\";\r\n\t\t\tpath = ../src;\r\n\t\t\tsourceTree = \"<group>\";\r\n\t\t};\r\n\t\t219F66186DDF392149043810 /* Products */ = {\r\n\t\t\tisa = PBXGroup;\r\n\t\t\tchildren = (\r\n\t\t\t\t1DA04ADC64C3566D16C45B6D /* libpugixmld.a */,\r\n\t\t\t);\r\n\t\t\tname = \"Products\";\r\n\t\t\tsourceTree = \"<group>\";\r\n\t\t};\r\n/* End PBXGroup section */\r\n\r\n/* Begin PBXNativeTarget section */\r\n\t\t6B55152571905B6C3A6F39D0 /* pugixml */ = {\r\n\t\t\tisa = PBXNativeTarget;\r\n\t\t\tbuildConfigurationList = 73BF376C14AA1ECC0AC517ED /* Build configuration list for PBXNativeTarget \"pugixml\" */;\r\n\t\t\tbuildPhases = (\r\n\t\t\t\t6CA66B9B6252229A36E8733C /* Resources */,\r\n\t\t\t\t287808486FBF545206A47CC1 /* Sources */,\r\n\t\t\t\t2BA00212518037166623673F /* Frameworks */,\r\n\t\t\t);\r\n\t\t\tbuildRules = (\r\n\t\t\t);\r\n\t\t\tdependencies = (\r\n\t\t\t);\r\n\t\t\tname = \"pugixml\";\r\n\t\t\tproductName = \"pugixml\";\r\n\t\t\tproductReference = 1DA04ADC64C3566D16C45B6D /* libpugixmld.a */;\r\n\t\t\tproductType = \"com.apple.product-type.library.static\";\r\n\t\t};\r\n/* End PBXNativeTarget section */\r\n\r\n/* Begin PBXProject section */\r\n\t\t08FB7793FE84155DC02AAC07 /* Project object */ = {\r\n\t\t\tisa = PBXProject;\r\n\t\t\tbuildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject \"pugixml\" */;\r\n\t\t\tcompatibilityVersion = \"Xcode 3.1\";\r\n\t\t\thasScannedForEncodings = 1;\r\n\t\t\tmainGroup = 19E0517F3CF26ED63AE23641 /* pugixml */;\r\n\t\t\tprojectDirPath = \"\";\r\n\t\t\tprojectRoot = \"\";\r\n\t\t\ttargets = (\r\n\t\t\t\t6B55152571905B6C3A6F39D0 /* libpugixmld.a */,\r\n\t\t\t);\r\n\t\t};\r\n/* End PBXProject section */\r\n\r\n/* Begin PBXResourcesBuildPhase section */\r\n\t\t6CA66B9B6252229A36E8733C /* Resources */ = {\r\n\t\t\tisa = PBXResourcesBuildPhase;\r\n\t\t\tbuildActionMask = 2147483647;\r\n\t\t\tfiles = (\r\n\t\t\t);\r\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\r\n\t\t};\r\n/* End PBXResourcesBuildPhase section */\r\n\r\n/* Begin PBXSourcesBuildPhase section */\r\n\t\t287808486FBF545206A47CC1 /* Sources */ = {\r\n\t\t\tisa = PBXSourcesBuildPhase;\r\n\t\t\tbuildActionMask = 2147483647;\r\n\t\t\tfiles = (\r\n\t\t\t\t0424128F67AB5C730232235E /* pugixml.cpp in Sources */,\r\n\t\t\t);\r\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\r\n\t\t};\r\n/* End PBXSourcesBuildPhase section */\r\n\r\n/* Begin PBXVariantGroup section */\r\n/* End PBXVariantGroup section */\r\n\r\n/* Begin XCBuildConfiguration section */\r\n\t\t4FDB54E4253E36FC55CE27E8 /* Debug */ = {\r\n\t\t\tisa = XCBuildConfiguration;\r\n\t\t\tbuildSettings = {\r\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\r\n\t\t\t\tCONFIGURATION_BUILD_DIR = xcode3;\r\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\r\n\t\t\t\tGCC_MODEL_TUNING = G5;\r\n\t\t\t\tINSTALL_PATH = /usr/local/lib;\r\n\t\t\t\tPRODUCT_NAME = \"pugixmld\";\r\n\t\t\t};\r\n\t\t\tname = \"Debug\";\r\n\t\t};\r\n\t\t0A4C28F553990E0405306C15 /* Release */ = {\r\n\t\t\tisa = XCBuildConfiguration;\r\n\t\t\tbuildSettings = {\r\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\r\n\t\t\t\tCONFIGURATION_BUILD_DIR = xcode3;\r\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\r\n\t\t\t\tGCC_MODEL_TUNING = G5;\r\n\t\t\t\tINSTALL_PATH = /usr/local/lib;\r\n\t\t\t\tPRODUCT_NAME = \"pugixml\";\r\n\t\t\t};\r\n\t\t\tname = \"Release\";\r\n\t\t};\r\n\t\t65DB0F6D27EA20852B6E3BB4 /* Debug */ = {\r\n\t\t\tisa = XCBuildConfiguration;\r\n\t\t\tbuildSettings = {\r\n\t\t\t\tARCHS = \"$(ARCHS_STANDARD_32_64_BIT)\";\r\n\t\t\t\tCONFIGURATION_BUILD_DIR = \"$(SYMROOT)\";\r\n\t\t\t\tCONFIGURATION_TEMP_DIR = \"$(OBJROOT)\";\r\n\t\t\t\tCOPY_PHASE_STRIP = NO;\r\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\r\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\r\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\r\n\t\t\t\t\t\"_DEBUG\",\r\n\t\t\t\t);\r\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES;\r\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\r\n\t\t\t\tOBJROOT = \"xcode3/Universal/Debug\";\r\n\t\t\t\tONLY_ACTIVE_ARCH = NO;\r\n\t\t\t\tPREBINDING = NO;\r\n\t\t\t\tSYMROOT = \"xcode3\";\r\n\t\t\t};\r\n\t\t\tname = \"Debug\";\r\n\t\t};\r\n\t\t5314084032B57C1A11945858 /* Release */ = {\r\n\t\t\tisa = XCBuildConfiguration;\r\n\t\t\tbuildSettings = {\r\n\t\t\t\tARCHS = \"$(ARCHS_STANDARD_32_64_BIT)\";\r\n\t\t\t\tCONFIGURATION_BUILD_DIR = \"$(SYMROOT)\";\r\n\t\t\t\tCONFIGURATION_TEMP_DIR = \"$(OBJROOT)\";\r\n\t\t\t\tCOPY_PHASE_STRIP = NO;\r\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\r\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = s;\r\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\r\n\t\t\t\t\t\"NDEBUG\",\r\n\t\t\t\t);\r\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES;\r\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\r\n\t\t\t\tOBJROOT = \"xcode3/Universal/Release\";\r\n\t\t\t\tONLY_ACTIVE_ARCH = NO;\r\n\t\t\t\tPREBINDING = NO;\r\n\t\t\t\tSYMROOT = \"xcode3\";\r\n\t\t\t};\r\n\t\t\tname = \"Release\";\r\n\t\t};\r\n/* End XCBuildConfiguration section */\r\n\r\n/* Begin XCConfigurationList section */\r\n\t\t73BF376C14AA1ECC0AC517ED /* Build configuration list for PBXNativeTarget \"libpugixmld.a\" */ = {\r\n\t\t\tisa = XCConfigurationList;\r\n\t\t\tbuildConfigurations = (\r\n\t\t\t\t4FDB54E4253E36FC55CE27E8 /* Debug */,\r\n\t\t\t\t0A4C28F553990E0405306C15 /* Release */,\r\n\t\t\t);\r\n\t\t\tdefaultConfigurationIsVisible = 0;\r\n\t\t\tdefaultConfigurationName = \"Debug\";\r\n\t\t};\r\n\t\t1DEB928908733DD80010E9CD /* Build configuration list for PBXProject \"pugixml\" */ = {\r\n\t\t\tisa = XCConfigurationList;\r\n\t\t\tbuildConfigurations = (\r\n\t\t\t\t65DB0F6D27EA20852B6E3BB4 /* Debug */,\r\n\t\t\t\t5314084032B57C1A11945858 /* Release */,\r\n\t\t\t);\r\n\t\t\tdefaultConfigurationIsVisible = 0;\r\n\t\t\tdefaultConfigurationName = \"Debug\";\r\n\t\t};\r\n/* End XCConfigurationList section */\r\n\r\n\t};\r\n\trootObject = 08FB7793FE84155DC02AAC07 /* Project object */;\r\n}\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_airplay.mkf",
    "content": "includepaths\r\n{\r\n\"../src\"\r\n}\r\n\r\nfiles\r\n{\r\n(\"../src\")\r\npugiconfig.hpp\r\npugixml.cpp\r\npugixml.hpp\r\n}\r\n\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_codeblocks.cbp",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\r\n<CodeBlocks_project_file>\r\n\t<FileVersion major=\"1\" minor=\"6\" />\r\n\t<Project>\r\n\t\t<Option title=\"pugixml\" />\r\n\t\t<Option pch_mode=\"2\" />\r\n\t\t<Option compiler=\"gcc\" />\r\n\t\t<Build>\r\n\t\t\t<Target title=\"Debug\">\r\n\t\t\t\t<Option output=\"codeblocks/libpugixmld.a\" prefix_auto=\"0\" extension_auto=\"0\" />\r\n\t\t\t\t<Option object_output=\"codeblocks/Debug\" />\r\n\t\t\t\t<Option type=\"2\" />\r\n\t\t\t\t<Option compiler=\"gcc\" />\r\n\t\t\t\t<Compiler>\r\n\t\t\t\t\t<Add option=\"-g\" />\r\n\t\t\t\t\t<Add option=\"-D_DEBUG\" />\r\n\t\t\t\t</Compiler>\r\n\t\t\t\t<Linker>\r\n\t\t\t\t</Linker>\r\n\t\t\t</Target>\r\n\t\t\t<Target title=\"Release\">\r\n\t\t\t\t<Option output=\"codeblocks/libpugixml.a\" prefix_auto=\"0\" extension_auto=\"0\" />\r\n\t\t\t\t<Option object_output=\"codeblocks/Release\" />\r\n\t\t\t\t<Option type=\"2\" />\r\n\t\t\t\t<Option compiler=\"gcc\" />\r\n\t\t\t\t<Compiler>\r\n\t\t\t\t\t<Add option=\"-g\" />\r\n\t\t\t\t\t<Add option=\"-O2\" />\r\n\t\t\t\t\t<Add option=\"-DNDEBUG\" />\r\n\t\t\t\t</Compiler>\r\n\t\t\t\t<Linker>\r\n\t\t\t\t</Linker>\r\n\t\t\t</Target>\r\n\t\t</Build>\r\n\t\t<Unit filename=\"../src/pugixml.hpp\">\r\n\t\t</Unit>\r\n\t\t<Unit filename=\"../src/pugiconfig.hpp\">\r\n\t\t</Unit>\r\n\t\t<Unit filename=\"../src/pugixml.cpp\">\r\n\t\t</Unit>\r\n\t\t<Extensions />\r\n\t</Project>\r\n</CodeBlocks_project_file>\r\n\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_codelite.project",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<CodeLite_Project Name=\"pugixml\">\r\n  <VirtualDirectory Name=\"src\">\r\n    <File Name=\"../src/pugixml.hpp\"/>\r\n    <File Name=\"../src/pugiconfig.hpp\"/>\r\n    <File Name=\"../src/pugixml.cpp\"/>\r\n  </VirtualDirectory>\r\n  <Settings Type=\"Static Library\">\r\n    <Configuration Name=\"Debug\" CompilerType=\"gnu g++\" DebuggerType=\"GNU gdb debugger\" Type=\"Static Library\">\r\n      <General OutputFile=\"codelite/libpugixmld.a\" IntermediateDirectory=\"codelite/Debug\" Command=\"./libpugixmld.a\" CommandArguments=\"\" WorkingDirectory=\"codelite\" PauseExecWhenProcTerminates=\"yes\"/>\r\n      <Compiler Required=\"yes\" Options=\"-g\">\r\n        <Preprocessor Value=\"_DEBUG\"/>\r\n      </Compiler>\r\n      <Linker Required=\"yes\" Options=\"\">\r\n      </Linker>\r\n      <ResourceCompiler Required=\"no\" Options=\"\"/>\r\n      <CustomBuild Enabled=\"no\">\r\n        <CleanCommand></CleanCommand>\r\n        <BuildCommand></BuildCommand>\r\n        <SingleFileCommand></SingleFileCommand>\r\n        <MakefileGenerationCommand></MakefileGenerationCommand>\r\n        <ThirdPartyToolName>None</ThirdPartyToolName>\r\n        <WorkingDirectory></WorkingDirectory>\r\n      </CustomBuild>\r\n      <AdditionalRules>\r\n        <CustomPostBuild></CustomPostBuild>\r\n        <CustomPreBuild></CustomPreBuild>\r\n      </AdditionalRules>\r\n    </Configuration>\r\n    <Configuration Name=\"Release\" CompilerType=\"gnu g++\" DebuggerType=\"GNU gdb debugger\" Type=\"Static Library\">\r\n      <General OutputFile=\"codelite/libpugixml.a\" IntermediateDirectory=\"codelite/Release\" Command=\"./libpugixml.a\" CommandArguments=\"\" WorkingDirectory=\"codelite\" PauseExecWhenProcTerminates=\"yes\"/>\r\n      <Compiler Required=\"yes\" Options=\"-g;-O2\">\r\n        <Preprocessor Value=\"NDEBUG\"/>\r\n      </Compiler>\r\n      <Linker Required=\"yes\" Options=\"\">\r\n      </Linker>\r\n      <ResourceCompiler Required=\"no\" Options=\"\"/>\r\n      <CustomBuild Enabled=\"no\">\r\n        <CleanCommand></CleanCommand>\r\n        <BuildCommand></BuildCommand>\r\n        <SingleFileCommand></SingleFileCommand>\r\n        <MakefileGenerationCommand></MakefileGenerationCommand>\r\n        <ThirdPartyToolName>None</ThirdPartyToolName>\r\n        <WorkingDirectory></WorkingDirectory>\r\n      </CustomBuild>\r\n      <AdditionalRules>\r\n        <CustomPostBuild></CustomPostBuild>\r\n        <CustomPreBuild></CustomPreBuild>\r\n      </AdditionalRules>\r\n    </Configuration>\r\n  </Settings>\r\n  <Dependencies name=\"Debug\">\r\n  </Dependencies>\r\n  <Dependencies name=\"Release\">\r\n  </Dependencies>\r\n</CodeLite_Project>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_dll.rc",
    "content": "#include <winver.h>\r\n\r\n#define PUGIXML_VERSION_MAJOR 1\r\n#define PUGIXML_VERSION_MINOR 14\r\n#define PUGIXML_VERSION_PATCH 0\r\n#define PUGIXML_VERSION_NUMBER \"1.14.0\\0\"\r\n\r\n#if defined(GCC_WINDRES) || defined(__MINGW32__) || defined(__CYGWIN__)\r\nVS_VERSION_INFO\t\tVERSIONINFO\r\n#else\r\nVS_VERSION_INFO\t\tVERSIONINFO\tMOVEABLE IMPURE LOADONCALL DISCARDABLE\r\n#endif\r\n  FILEVERSION\t\tPUGIXML_VERSION_MAJOR,PUGIXML_VERSION_MINOR,PUGIXML_VERSION_PATCH,0\r\n  PRODUCTVERSION\tPUGIXML_VERSION_MAJOR,PUGIXML_VERSION_MINOR,PUGIXML_VERSION_PATCH,0\r\n  FILEFLAGSMASK\t\tVS_FFI_FILEFLAGSMASK\r\n#ifdef _DEBUG\r\n  FILEFLAGS\t\t1\r\n#else\r\n  FILEFLAGS\t\t0\r\n#endif\r\n  FILEOS\t\tVOS__WINDOWS32\r\n  FILETYPE\t\tVFT_DLL\r\n  FILESUBTYPE\t\t0\t// not used\r\nBEGIN\r\n  BLOCK \"StringFileInfo\"\r\n  BEGIN\r\n    BLOCK \"040904E4\"\r\n    //language ID = U.S. English, char set = Windows, Multilingual\r\n    BEGIN\r\n      VALUE \"CompanyName\",\t\"zeux/pugixml\\0\"\r\n      VALUE \"FileDescription\",\t\"pugixml library\\0\"\r\n      VALUE \"FileVersion\",\tPUGIXML_VERSION_NUMBER\r\n      VALUE \"InternalName\",\t\"pugixml.dll\\0\"\r\n      VALUE \"LegalCopyright\",\t\"Copyright (C) 2006-2023, by Arseny Kapoulkine\\0\"\r\n      VALUE \"OriginalFilename\",\t\"pugixml.dll\\0\"\r\n      VALUE \"ProductName\",\t\"pugixml\\0\"\r\n      VALUE \"ProductVersion\",\tPUGIXML_VERSION_NUMBER\r\n      VALUE \"Comments\",\t\t\"For more information visit https://github.com/zeux/pugixml/\\0\"\r\n    END\r\n  END\r\n  BLOCK \"VarFileInfo\"\r\n  BEGIN\r\n    VALUE \"Translation\", 0x0409, 1252\r\n  END\r\nEND\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2005.vcproj",
    "content": "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\r\n<VisualStudioProject\r\n\tProjectType=\"Visual C++\"\r\n\tVersion=\"8.00\"\r\n\tName=\"pugixml\"\r\n\tProjectGUID=\"{89A1E353-E2DC-495C-B403-742BE206ACED}\"\r\n\tRootNamespace=\"pugixml\"\r\n\tKeyword=\"Win32Proj\"\r\n\t>\r\n\t<Platforms>\r\n\t\t<Platform\r\n\t\t\tName=\"Win32\"\r\n\t\t/>\r\n\t\t<Platform\r\n\t\t\tName=\"x64\"\r\n\t\t/>\r\n\t</Platforms>\r\n\t<ToolFiles>\r\n\t</ToolFiles>\r\n\t<Configurations>\r\n\t\t<Configuration\r\n\t\t\tName=\"Debug|Win32\"\r\n\t\t\tOutputDirectory=\"vs2005\\x32\"\r\n\t\t\tIntermediateDirectory=\"vs2005\\x32\\Debug\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"0\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t\tBasicRuntimeChecks=\"3\"\r\n\t\t\t\tRuntimeLibrary=\"3\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmld.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmld.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Debug|x64\"\r\n\t\t\tOutputDirectory=\"vs2005\\x64\"\r\n\t\t\tIntermediateDirectory=\"vs2005\\x64\\Debug\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t\tTargetEnvironment=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"0\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t\tBasicRuntimeChecks=\"3\"\r\n\t\t\t\tRuntimeLibrary=\"3\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmld.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmld.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Release|Win32\"\r\n\t\t\tOutputDirectory=\"vs2005\\x32\"\r\n\t\t\tIntermediateDirectory=\"vs2005\\x32\\Release\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t\tStringPooling=\"true\"\r\n\t\t\t\tRuntimeLibrary=\"2\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixml.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixml.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Release|x64\"\r\n\t\t\tOutputDirectory=\"vs2005\\x64\"\r\n\t\t\tIntermediateDirectory=\"vs2005\\x64\\Release\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t\tTargetEnvironment=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t\tStringPooling=\"true\"\r\n\t\t\t\tRuntimeLibrary=\"2\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixml.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixml.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t</Configurations>\r\n\t<References>\r\n\t</References>\r\n\t<Files>\r\n\t\t<Filter\r\n\t\t\tName=\"src\"\r\n\t\t\tFilter=\"\"\r\n\t\t\t>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugixml.hpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugiconfig.hpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugixml.cpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t</Filter>\r\n\t</Files>\r\n\t<Globals>\r\n\t</Globals>\r\n</VisualStudioProject>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2005_static.vcproj",
    "content": "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\r\n<VisualStudioProject\r\n\tProjectType=\"Visual C++\"\r\n\tVersion=\"8.00\"\r\n\tName=\"pugixml\"\r\n\tProjectGUID=\"{89A1E353-E2DC-495C-B403-742BE206ACED}\"\r\n\tRootNamespace=\"pugixml\"\r\n\tKeyword=\"Win32Proj\"\r\n\t>\r\n\t<Platforms>\r\n\t\t<Platform\r\n\t\t\tName=\"Win32\"\r\n\t\t/>\r\n\t\t<Platform\r\n\t\t\tName=\"x64\"\r\n\t\t/>\r\n\t</Platforms>\r\n\t<ToolFiles>\r\n\t</ToolFiles>\r\n\t<Configurations>\r\n\t\t<Configuration\r\n\t\t\tName=\"Debug|Win32\"\r\n\t\t\tOutputDirectory=\"vs2005\\x32\"\r\n\t\t\tIntermediateDirectory=\"vs2005\\x32\\DebugStatic\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"0\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t\tBasicRuntimeChecks=\"3\"\r\n\t\t\t\tRuntimeLibrary=\"1\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmlsd.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmlsd.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Debug|x64\"\r\n\t\t\tOutputDirectory=\"vs2005\\x64\"\r\n\t\t\tIntermediateDirectory=\"vs2005\\x64\\DebugStatic\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t\tTargetEnvironment=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"0\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t\tBasicRuntimeChecks=\"3\"\r\n\t\t\t\tRuntimeLibrary=\"1\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmlsd.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmlsd.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Release|Win32\"\r\n\t\t\tOutputDirectory=\"vs2005\\x32\"\r\n\t\t\tIntermediateDirectory=\"vs2005\\x32\\ReleaseStatic\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t\tStringPooling=\"true\"\r\n\t\t\t\tRuntimeLibrary=\"0\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmls.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmls.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Release|x64\"\r\n\t\t\tOutputDirectory=\"vs2005\\x64\"\r\n\t\t\tIntermediateDirectory=\"vs2005\\x64\\ReleaseStatic\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t\tTargetEnvironment=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t\tStringPooling=\"true\"\r\n\t\t\t\tRuntimeLibrary=\"0\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tDetect64BitPortabilityProblems=\"true\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmls.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmls.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t</Configurations>\r\n\t<References>\r\n\t</References>\r\n\t<Files>\r\n\t\t<Filter\r\n\t\t\tName=\"src\"\r\n\t\t\tFilter=\"\"\r\n\t\t\t>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugixml.hpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugiconfig.hpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugixml.cpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t</Filter>\r\n\t</Files>\r\n\t<Globals>\r\n\t</Globals>\r\n</VisualStudioProject>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2008.vcproj",
    "content": "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\r\n<VisualStudioProject\r\n\tProjectType=\"Visual C++\"\r\n\tVersion=\"9.00\"\r\n\tName=\"pugixml\"\r\n\tProjectGUID=\"{89A1E353-E2DC-495C-B403-742BE206ACED}\"\r\n\tRootNamespace=\"pugixml\"\r\n\tKeyword=\"Win32Proj\"\r\n\t>\r\n\t<Platforms>\r\n\t\t<Platform\r\n\t\t\tName=\"Win32\"\r\n\t\t/>\r\n\t\t<Platform\r\n\t\t\tName=\"x64\"\r\n\t\t/>\r\n\t</Platforms>\r\n\t<ToolFiles>\r\n\t</ToolFiles>\r\n\t<Configurations>\r\n\t\t<Configuration\r\n\t\t\tName=\"Debug|Win32\"\r\n\t\t\tOutputDirectory=\"vs2008\\x32\"\r\n\t\t\tIntermediateDirectory=\"vs2008\\x32\\Debug\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"0\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t\tBasicRuntimeChecks=\"3\"\r\n\t\t\t\tRuntimeLibrary=\"3\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmld.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmld.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Debug|x64\"\r\n\t\t\tOutputDirectory=\"vs2008\\x64\"\r\n\t\t\tIntermediateDirectory=\"vs2008\\x64\\Debug\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t\tTargetEnvironment=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"0\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t\tBasicRuntimeChecks=\"3\"\r\n\t\t\t\tRuntimeLibrary=\"3\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmld.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmld.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Release|Win32\"\r\n\t\t\tOutputDirectory=\"vs2008\\x32\"\r\n\t\t\tIntermediateDirectory=\"vs2008\\x32\\Release\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t\tStringPooling=\"true\"\r\n\t\t\t\tRuntimeLibrary=\"2\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixml.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixml.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Release|x64\"\r\n\t\t\tOutputDirectory=\"vs2008\\x64\"\r\n\t\t\tIntermediateDirectory=\"vs2008\\x64\\Release\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t\tTargetEnvironment=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t\tStringPooling=\"true\"\r\n\t\t\t\tRuntimeLibrary=\"2\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixml.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixml.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t</Configurations>\r\n\t<References>\r\n\t</References>\r\n\t<Files>\r\n\t\t<Filter\r\n\t\t\tName=\"src\"\r\n\t\t\tFilter=\"\"\r\n\t\t\t>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugixml.hpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugiconfig.hpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugixml.cpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t</Filter>\r\n\t</Files>\r\n\t<Globals>\r\n\t</Globals>\r\n</VisualStudioProject>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2008_static.vcproj",
    "content": "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\r\n<VisualStudioProject\r\n\tProjectType=\"Visual C++\"\r\n\tVersion=\"9.00\"\r\n\tName=\"pugixml\"\r\n\tProjectGUID=\"{89A1E353-E2DC-495C-B403-742BE206ACED}\"\r\n\tRootNamespace=\"pugixml\"\r\n\tKeyword=\"Win32Proj\"\r\n\t>\r\n\t<Platforms>\r\n\t\t<Platform\r\n\t\t\tName=\"Win32\"\r\n\t\t/>\r\n\t\t<Platform\r\n\t\t\tName=\"x64\"\r\n\t\t/>\r\n\t</Platforms>\r\n\t<ToolFiles>\r\n\t</ToolFiles>\r\n\t<Configurations>\r\n\t\t<Configuration\r\n\t\t\tName=\"Debug|Win32\"\r\n\t\t\tOutputDirectory=\"vs2008\\x32\"\r\n\t\t\tIntermediateDirectory=\"vs2008\\x32\\DebugStatic\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"0\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t\tBasicRuntimeChecks=\"3\"\r\n\t\t\t\tRuntimeLibrary=\"1\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmlsd.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmlsd.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Debug|x64\"\r\n\t\t\tOutputDirectory=\"vs2008\\x64\"\r\n\t\t\tIntermediateDirectory=\"vs2008\\x64\\DebugStatic\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t\tTargetEnvironment=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"0\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t\tBasicRuntimeChecks=\"3\"\r\n\t\t\t\tRuntimeLibrary=\"1\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmlsd.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"_DEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmlsd.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Release|Win32\"\r\n\t\t\tOutputDirectory=\"vs2008\\x32\"\r\n\t\t\tIntermediateDirectory=\"vs2008\\x32\\ReleaseStatic\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t\tStringPooling=\"true\"\r\n\t\t\t\tRuntimeLibrary=\"0\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmls.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmls.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t\t<Configuration\r\n\t\t\tName=\"Release|x64\"\r\n\t\t\tOutputDirectory=\"vs2008\\x64\"\r\n\t\t\tIntermediateDirectory=\"vs2008\\x64\\ReleaseStatic\"\r\n\t\t\tConfigurationType=\"4\"\r\n\t\t\tCharacterSet=\"2\"\r\n\t\t\t>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreBuildEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCustomBuildTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXMLDataGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCMIDLTool\"\r\n\t\t\t\tTargetEnvironment=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCCLCompilerTool\"\r\n\t\t\t\tOptimization=\"3\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t\tStringPooling=\"true\"\r\n\t\t\t\tRuntimeLibrary=\"0\"\r\n\t\t\t\tEnableFunctionLevelLinking=\"true\"\r\n\t\t\t\tUsePrecompiledHeader=\"0\"\r\n\t\t\t\tWarningLevel=\"3\"\r\n\t\t\t\tProgramDataBaseFileName=\"$(OutDir)\\pugixmls.pdb\"\r\n\t\t\t\tDebugInformationFormat=\"3\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManagedResourceCompilerTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCResourceCompilerTool\"\r\n\t\t\t\tPreprocessorDefinitions=\"NDEBUG\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPreLinkEventTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCLibrarianTool\"\r\n\t\t\t\tOutputFile=\"$(OutDir)\\pugixmls.lib\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCALinkTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCManifestTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCXDCMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCBscMakeTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCFxCopTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCAppVerifierTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCWebDeploymentTool\"\r\n\t\t\t/>\r\n\t\t\t<Tool\r\n\t\t\t\tName=\"VCPostBuildEventTool\"\r\n\t\t\t/>\r\n\t\t</Configuration>\r\n\t</Configurations>\r\n\t<References>\r\n\t</References>\r\n\t<Files>\r\n\t\t<Filter\r\n\t\t\tName=\"src\"\r\n\t\t\tFilter=\"\"\r\n\t\t\t>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugixml.hpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugiconfig.hpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t\t<File\r\n\t\t\t\tRelativePath=\"..\\src\\pugixml.cpp\"\r\n\t\t\t\t>\r\n\t\t\t</File>\r\n\t\t</Filter>\r\n\t</Files>\r\n\t<Globals>\r\n\t</Globals>\r\n</VisualStudioProject>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2010.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n\t<ItemGroup Label=\"ProjectConfigurations\">\r\n\t\t<ProjectConfiguration Include=\"Debug|Win32\">\r\n\t\t\t<Configuration>Debug</Configuration>\r\n\t\t\t<Platform>Win32</Platform>\r\n\t\t</ProjectConfiguration>\r\n\t\t<ProjectConfiguration Include=\"Debug|x64\">\r\n\t\t\t<Configuration>Debug</Configuration>\r\n\t\t\t<Platform>x64</Platform>\r\n\t\t</ProjectConfiguration>\r\n\t\t<ProjectConfiguration Include=\"Release|Win32\">\r\n\t\t\t<Configuration>Release</Configuration>\r\n\t\t\t<Platform>Win32</Platform>\r\n\t\t</ProjectConfiguration>\r\n\t\t<ProjectConfiguration Include=\"Release|x64\">\r\n\t\t\t<Configuration>Release</Configuration>\r\n\t\t\t<Platform>x64</Platform>\r\n\t\t</ProjectConfiguration>\r\n\t</ItemGroup>\r\n\t<PropertyGroup Label=\"Globals\">\r\n\t\t<ProjectGuid>{89A1E353-E2DC-495C-B403-742BE206ACED}</ProjectGuid>\r\n\t\t<RootNamespace>pugixml</RootNamespace>\r\n\t\t<Keyword>Win32Proj</Keyword>\r\n\t</PropertyGroup>\r\n\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n\t\t<ConfigurationType>StaticLibrary</ConfigurationType>\r\n\t\t<CharacterSet>MultiByte</CharacterSet>\r\n\t\t<UseDebugLibraries>true</UseDebugLibraries>\r\n\t</PropertyGroup>\r\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n\t\t<ConfigurationType>StaticLibrary</ConfigurationType>\r\n\t\t<CharacterSet>MultiByte</CharacterSet>\r\n\t\t<UseDebugLibraries>true</UseDebugLibraries>\r\n\t</PropertyGroup>\r\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n\t\t<ConfigurationType>StaticLibrary</ConfigurationType>\r\n\t\t<CharacterSet>MultiByte</CharacterSet>\r\n\t\t<WholeProgramOptimization>true</WholeProgramOptimization>\r\n\t\t<UseDebugLibraries>false</UseDebugLibraries>\r\n\t</PropertyGroup>\r\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n\t\t<ConfigurationType>StaticLibrary</ConfigurationType>\r\n\t\t<CharacterSet>MultiByte</CharacterSet>\r\n\t\t<WholeProgramOptimization>true</WholeProgramOptimization>\r\n\t\t<UseDebugLibraries>false</UseDebugLibraries>\r\n\t</PropertyGroup>\r\n\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n\t<ImportGroup Label=\"ExtensionSettings\">\r\n\t</ImportGroup>\r\n\t<ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"PropertySheets\">\r\n\t\t<Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n\t</ImportGroup>\r\n\t<ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"PropertySheets\">\r\n\t\t<Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n\t</ImportGroup>\r\n\t<ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"PropertySheets\">\r\n\t\t<Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n\t</ImportGroup>\r\n\t<ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"PropertySheets\">\r\n\t\t<Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n\t</ImportGroup>\r\n\t<PropertyGroup Label=\"UserMacros\" />\r\n\t<PropertyGroup>\r\n\t\t<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r\n\t\t<OutDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">vs2010\\Win32_Debug\\</OutDir>\r\n\t\t<IntDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">vs2010\\Win32_Debug\\</IntDir>\r\n\t\t<TargetName Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">pugixml</TargetName>\r\n\t\t<OutDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">vs2010\\x64_Debug\\</OutDir>\r\n\t\t<IntDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">vs2010\\x64_Debug\\</IntDir>\r\n\t\t<TargetName Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">pugixml</TargetName>\r\n\t\t<OutDir Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">vs2010\\Win32_Release\\</OutDir>\r\n\t\t<IntDir Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">vs2010\\Win32_Release\\</IntDir>\r\n\t\t<TargetName Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">pugixml</TargetName>\r\n\t\t<OutDir Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">vs2010\\x64_Release\\</OutDir>\r\n\t\t<IntDir Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">vs2010\\x64_Release\\</IntDir>\r\n\t\t<TargetName Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">pugixml</TargetName>\r\n\t</PropertyGroup>\r\n\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n\t\t<ClCompile>\r\n\t\t\t<Optimization>Disabled</Optimization>\r\n\t\t\t<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t\t<MinimalRebuild>false</MinimalRebuild>\r\n\t\t\t<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n\t\t\t<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r\n\t\t\t<FunctionLevelLinking>true</FunctionLevelLinking>\r\n\t\t\t<PrecompiledHeader></PrecompiledHeader>\r\n\t\t\t<WarningLevel>Level3</WarningLevel>\r\n\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n\t\t</ClCompile>\r\n\t\t<ResourceCompile>\r\n\t\t\t<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t</ResourceCompile>\r\n\t<Lib>\r\n\t\t<OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n\t</Lib>\r\n\t\t<Link>\r\n\t\t\t<SubSystem>Windows</SubSystem>\r\n\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\r\n\t\t\t<ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n\t\t</Link>\r\n\t</ItemDefinitionGroup>\r\n\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n\t\t<ClCompile>\r\n\t\t\t<Optimization>Disabled</Optimization>\r\n\t\t\t<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t\t<MinimalRebuild>false</MinimalRebuild>\r\n\t\t\t<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n\t\t\t<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r\n\t\t\t<FunctionLevelLinking>true</FunctionLevelLinking>\r\n\t\t\t<PrecompiledHeader></PrecompiledHeader>\r\n\t\t\t<WarningLevel>Level3</WarningLevel>\r\n\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n\t\t</ClCompile>\r\n\t\t<ResourceCompile>\r\n\t\t\t<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t</ResourceCompile>\r\n\t<Lib>\r\n\t\t<OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n\t</Lib>\r\n\t\t<Link>\r\n\t\t\t<SubSystem>Windows</SubSystem>\r\n\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\r\n\t\t\t<ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n\t\t</Link>\r\n\t</ItemDefinitionGroup>\r\n\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n\t\t<ClCompile>\r\n\t\t\t<Optimization>Full</Optimization>\r\n\t\t\t<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t\t<MinimalRebuild>false</MinimalRebuild>\r\n\t\t\t<StringPooling>true</StringPooling>\r\n\t\t\t<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r\n\t\t\t<FunctionLevelLinking>true</FunctionLevelLinking>\r\n\t\t\t<PrecompiledHeader></PrecompiledHeader>\r\n\t\t\t<WarningLevel>Level3</WarningLevel>\r\n\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n\t\t</ClCompile>\r\n\t\t<ResourceCompile>\r\n\t\t\t<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t</ResourceCompile>\r\n\t<Lib>\r\n\t\t<OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n\t</Lib>\r\n\t\t<Link>\r\n\t\t\t<SubSystem>Windows</SubSystem>\r\n\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\r\n\t\t\t<OptimizeReferences>true</OptimizeReferences>\r\n\t\t\t<EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n\t\t\t<ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n\t\t</Link>\r\n\t</ItemDefinitionGroup>\r\n\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n\t\t<ClCompile>\r\n\t\t\t<Optimization>Full</Optimization>\r\n\t\t\t<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t\t<MinimalRebuild>false</MinimalRebuild>\r\n\t\t\t<StringPooling>true</StringPooling>\r\n\t\t\t<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r\n\t\t\t<FunctionLevelLinking>true</FunctionLevelLinking>\r\n\t\t\t<PrecompiledHeader></PrecompiledHeader>\r\n\t\t\t<WarningLevel>Level3</WarningLevel>\r\n\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n\t\t</ClCompile>\r\n\t\t<ResourceCompile>\r\n\t\t\t<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t</ResourceCompile>\r\n\t<Lib>\r\n\t\t<OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n\t</Lib>\r\n\t\t<Link>\r\n\t\t\t<SubSystem>Windows</SubSystem>\r\n\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\r\n\t\t\t<OptimizeReferences>true</OptimizeReferences>\r\n\t\t\t<EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n\t\t\t<ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n\t\t</Link>\r\n\t</ItemDefinitionGroup>\r\n\t<ItemGroup>\r\n\t\t<ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n\t\t<ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n\t</ItemGroup>\r\n\t<ItemGroup>\r\n\t\t<ClCompile Include=\"..\\src\\pugixml.cpp\">\r\n\t\t</ClCompile>\r\n\t</ItemGroup>\r\n\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n\t<ImportGroup Label=\"ExtensionTargets\">\r\n\t</ImportGroup>\r\n</Project>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2010_static.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n\t<ItemGroup Label=\"ProjectConfigurations\">\r\n\t\t<ProjectConfiguration Include=\"Debug|Win32\">\r\n\t\t\t<Configuration>Debug</Configuration>\r\n\t\t\t<Platform>Win32</Platform>\r\n\t\t</ProjectConfiguration>\r\n\t\t<ProjectConfiguration Include=\"Debug|x64\">\r\n\t\t\t<Configuration>Debug</Configuration>\r\n\t\t\t<Platform>x64</Platform>\r\n\t\t</ProjectConfiguration>\r\n\t\t<ProjectConfiguration Include=\"Release|Win32\">\r\n\t\t\t<Configuration>Release</Configuration>\r\n\t\t\t<Platform>Win32</Platform>\r\n\t\t</ProjectConfiguration>\r\n\t\t<ProjectConfiguration Include=\"Release|x64\">\r\n\t\t\t<Configuration>Release</Configuration>\r\n\t\t\t<Platform>x64</Platform>\r\n\t\t</ProjectConfiguration>\r\n\t</ItemGroup>\r\n\t<PropertyGroup Label=\"Globals\">\r\n\t\t<ProjectGuid>{89A1E353-E2DC-495C-B403-742BE206ACED}</ProjectGuid>\r\n\t\t<RootNamespace>pugixml</RootNamespace>\r\n\t\t<Keyword>Win32Proj</Keyword>\r\n\t</PropertyGroup>\r\n\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n\t\t<ConfigurationType>StaticLibrary</ConfigurationType>\r\n\t\t<CharacterSet>MultiByte</CharacterSet>\r\n\t\t<UseDebugLibraries>true</UseDebugLibraries>\r\n\t</PropertyGroup>\r\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n\t\t<ConfigurationType>StaticLibrary</ConfigurationType>\r\n\t\t<CharacterSet>MultiByte</CharacterSet>\r\n\t\t<UseDebugLibraries>true</UseDebugLibraries>\r\n\t</PropertyGroup>\r\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n\t\t<ConfigurationType>StaticLibrary</ConfigurationType>\r\n\t\t<CharacterSet>MultiByte</CharacterSet>\r\n\t\t<WholeProgramOptimization>true</WholeProgramOptimization>\r\n\t\t<UseDebugLibraries>false</UseDebugLibraries>\r\n\t</PropertyGroup>\r\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n\t\t<ConfigurationType>StaticLibrary</ConfigurationType>\r\n\t\t<CharacterSet>MultiByte</CharacterSet>\r\n\t\t<WholeProgramOptimization>true</WholeProgramOptimization>\r\n\t\t<UseDebugLibraries>false</UseDebugLibraries>\r\n\t</PropertyGroup>\r\n\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n\t<ImportGroup Label=\"ExtensionSettings\">\r\n\t</ImportGroup>\r\n\t<ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"PropertySheets\">\r\n\t\t<Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n\t</ImportGroup>\r\n\t<ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"PropertySheets\">\r\n\t\t<Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n\t</ImportGroup>\r\n\t<ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"PropertySheets\">\r\n\t\t<Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n\t</ImportGroup>\r\n\t<ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"PropertySheets\">\r\n\t\t<Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n\t</ImportGroup>\r\n\t<PropertyGroup Label=\"UserMacros\" />\r\n\t<PropertyGroup>\r\n\t\t<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r\n\t\t<OutDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">vs2010\\Win32_DebugStatic\\</OutDir>\r\n\t\t<IntDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">vs2010\\Win32_DebugStatic\\</IntDir>\r\n\t\t<TargetName Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">pugixml</TargetName>\r\n\t\t<OutDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">vs2010\\x64_DebugStatic\\</OutDir>\r\n\t\t<IntDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">vs2010\\x64_DebugStatic\\</IntDir>\r\n\t\t<TargetName Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">pugixml</TargetName>\r\n\t\t<OutDir Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">vs2010\\Win32_ReleaseStatic\\</OutDir>\r\n\t\t<IntDir Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">vs2010\\Win32_ReleaseStatic\\</IntDir>\r\n\t\t<TargetName Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">pugixml</TargetName>\r\n\t\t<OutDir Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">vs2010\\x64_ReleaseStatic\\</OutDir>\r\n\t\t<IntDir Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">vs2010\\x64_ReleaseStatic\\</IntDir>\r\n\t\t<TargetName Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">pugixml</TargetName>\r\n\t</PropertyGroup>\r\n\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n\t\t<ClCompile>\r\n\t\t\t<Optimization>Disabled</Optimization>\r\n\t\t\t<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t\t<MinimalRebuild>false</MinimalRebuild>\r\n\t\t\t<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n\t\t\t<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n\t\t\t<FunctionLevelLinking>true</FunctionLevelLinking>\r\n\t\t\t<PrecompiledHeader></PrecompiledHeader>\r\n\t\t\t<WarningLevel>Level3</WarningLevel>\r\n\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n\t\t</ClCompile>\r\n\t\t<ResourceCompile>\r\n\t\t\t<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t</ResourceCompile>\r\n\t<Lib>\r\n\t\t<OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n\t</Lib>\r\n\t\t<Link>\r\n\t\t\t<SubSystem>Windows</SubSystem>\r\n\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\r\n\t\t\t<ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n\t\t</Link>\r\n\t</ItemDefinitionGroup>\r\n\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n\t\t<ClCompile>\r\n\t\t\t<Optimization>Disabled</Optimization>\r\n\t\t\t<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t\t<MinimalRebuild>false</MinimalRebuild>\r\n\t\t\t<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n\t\t\t<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n\t\t\t<FunctionLevelLinking>true</FunctionLevelLinking>\r\n\t\t\t<PrecompiledHeader></PrecompiledHeader>\r\n\t\t\t<WarningLevel>Level3</WarningLevel>\r\n\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n\t\t</ClCompile>\r\n\t\t<ResourceCompile>\r\n\t\t\t<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t</ResourceCompile>\r\n\t<Lib>\r\n\t\t<OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n\t</Lib>\r\n\t\t<Link>\r\n\t\t\t<SubSystem>Windows</SubSystem>\r\n\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\r\n\t\t\t<ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n\t\t</Link>\r\n\t</ItemDefinitionGroup>\r\n\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n\t\t<ClCompile>\r\n\t\t\t<Optimization>Full</Optimization>\r\n\t\t\t<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t\t<MinimalRebuild>false</MinimalRebuild>\r\n\t\t\t<StringPooling>true</StringPooling>\r\n\t\t\t<RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n\t\t\t<FunctionLevelLinking>true</FunctionLevelLinking>\r\n\t\t\t<PrecompiledHeader></PrecompiledHeader>\r\n\t\t\t<WarningLevel>Level3</WarningLevel>\r\n\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n\t\t</ClCompile>\r\n\t\t<ResourceCompile>\r\n\t\t\t<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t</ResourceCompile>\r\n\t<Lib>\r\n\t\t<OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n\t</Lib>\r\n\t\t<Link>\r\n\t\t\t<SubSystem>Windows</SubSystem>\r\n\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\r\n\t\t\t<OptimizeReferences>true</OptimizeReferences>\r\n\t\t\t<EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n\t\t\t<ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n\t\t</Link>\r\n\t</ItemDefinitionGroup>\r\n\t<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n\t\t<ClCompile>\r\n\t\t\t<Optimization>Full</Optimization>\r\n\t\t\t<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t\t<MinimalRebuild>false</MinimalRebuild>\r\n\t\t\t<StringPooling>true</StringPooling>\r\n\t\t\t<RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n\t\t\t<FunctionLevelLinking>true</FunctionLevelLinking>\r\n\t\t\t<PrecompiledHeader></PrecompiledHeader>\r\n\t\t\t<WarningLevel>Level3</WarningLevel>\r\n\t\t\t<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n\t\t</ClCompile>\r\n\t\t<ResourceCompile>\r\n\t\t\t<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n\t\t</ResourceCompile>\r\n\t<Lib>\r\n\t\t<OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n\t</Lib>\r\n\t\t<Link>\r\n\t\t\t<SubSystem>Windows</SubSystem>\r\n\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\r\n\t\t\t<OptimizeReferences>true</OptimizeReferences>\r\n\t\t\t<EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n\t\t\t<ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n\t\t</Link>\r\n\t</ItemDefinitionGroup>\r\n\t<ItemGroup>\r\n\t\t<ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n\t\t<ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n\t</ItemGroup>\r\n\t<ItemGroup>\r\n\t\t<ClCompile Include=\"..\\src\\pugixml.cpp\">\r\n\t\t</ClCompile>\r\n\t</ItemGroup>\r\n\t<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n\t<ImportGroup Label=\"ExtensionTargets\">\r\n\t</ImportGroup>\r\n</Project>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2013.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{89A1E353-E2DC-495C-B403-742BE206ACED}</ProjectGuid>\r\n    <RootNamespace>pugixml</RootNamespace>\r\n    <Keyword>Win32Proj</Keyword>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup>\r\n    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r\n    <OutDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">vs2013\\Win32_Debug\\</OutDir>\r\n    <IntDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">vs2013\\Win32_Debug\\</IntDir>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">pugixml</TargetName>\r\n    <OutDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">vs2013\\x64_Debug\\</OutDir>\r\n    <IntDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">vs2013\\x64_Debug\\</IntDir>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">pugixml</TargetName>\r\n    <OutDir Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">vs2013\\Win32_Release\\</OutDir>\r\n    <IntDir Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">vs2013\\Win32_Release\\</IntDir>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">pugixml</TargetName>\r\n    <OutDir Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">vs2013\\x64_Release\\</OutDir>\r\n    <IntDir Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">vs2013\\x64_Release\\</IntDir>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Lib>\r\n      <OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n    </Lib>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Lib>\r\n      <OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n    </Lib>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <Optimization>Full</Optimization>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <StringPooling>true</StringPooling>\r\n      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Lib>\r\n      <OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n    </Lib>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <Optimization>Full</Optimization>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <StringPooling>true</StringPooling>\r\n      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Lib>\r\n      <OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n    </Lib>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n    <ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"..\\src\\pugixml.cpp\">\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2013_static.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{89A1E353-E2DC-495C-B403-742BE206ACED}</ProjectGuid>\r\n    <RootNamespace>pugixml</RootNamespace>\r\n    <Keyword>Win32Proj</Keyword>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v120</PlatformToolset>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup>\r\n    <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r\n    <OutDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">vs2013\\Win32_DebugStatic\\</OutDir>\r\n    <IntDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">vs2013\\Win32_DebugStatic\\</IntDir>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">pugixml</TargetName>\r\n    <OutDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">vs2013\\x64_DebugStatic\\</OutDir>\r\n    <IntDir Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">vs2013\\x64_DebugStatic\\</IntDir>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">pugixml</TargetName>\r\n    <OutDir Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">vs2013\\Win32_ReleaseStatic\\</OutDir>\r\n    <IntDir Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">vs2013\\Win32_ReleaseStatic\\</IntDir>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">pugixml</TargetName>\r\n    <OutDir Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">vs2013\\x64_ReleaseStatic\\</OutDir>\r\n    <IntDir Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">vs2013\\x64_ReleaseStatic\\</IntDir>\r\n    <TargetName Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Lib>\r\n      <OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n    </Lib>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Lib>\r\n      <OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n    </Lib>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <Optimization>Full</Optimization>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <StringPooling>true</StringPooling>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Lib>\r\n      <OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n    </Lib>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <Optimization>Full</Optimization>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <StringPooling>true</StringPooling>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <ResourceCompile>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Lib>\r\n      <OutputFile>$(OutDir)pugixml.lib</OutputFile>\r\n    </Lib>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <ProgramDataBaseFileName>$(OutDir)pugixml.pdb</ProgramDataBaseFileName>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n    <ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"..\\src\\pugixml.cpp\">\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2015.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"14.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{07CF01C0-B887-499D-AD9C-799CB6A9FE64}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>pugixml</RootNamespace>\r\n    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <OutDir>vs2015\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2015\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <OutDir>vs2015\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2015\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <OutDir>vs2015\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2015\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <OutDir>vs2015\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2015\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n    <ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"..\\src\\pugixml.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2015_static.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"14.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{07CF01C0-B887-499D-AD9C-799CB6A9FE64}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>pugixml</RootNamespace>\r\n    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n    <WholeProgramOptimization>true</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <OutDir>vs2015\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2015\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <OutDir>vs2015\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2015\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <OutDir>vs2015\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2015\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <OutDir>vs2015\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2015\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n    <ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"..\\src\\pugixml.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2017.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{07CF01C0-B887-499D-AD9C-799CB6A9FE64}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>pugixml</RootNamespace>\r\n    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <OutDir>vs2017\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2017\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <OutDir>vs2017\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2017\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <OutDir>vs2017\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2017\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <OutDir>vs2017\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2017\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n    <ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"..\\src\\pugixml.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2017_static.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{07CF01C0-B887-499D-AD9C-799CB6A9FE64}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>pugixml</RootNamespace>\r\n    <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v141</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <OutDir>vs2017\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2017\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <OutDir>vs2017\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2017\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <OutDir>vs2017\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2017\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <OutDir>vs2017\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2017\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n    <ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"..\\src\\pugixml.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2019.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{07CF01C0-B887-499D-AD9C-799CB6A9FE64}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>pugixml</RootNamespace>\r\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <OutDir>vs2019\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2019\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <OutDir>vs2019\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2019\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <OutDir>vs2019\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2019\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <OutDir>vs2019\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2019\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n    <ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"..\\src\\pugixml.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2019_static.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{07CF01C0-B887-499D-AD9C-799CB6A9FE64}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>pugixml</RootNamespace>\r\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v142</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <OutDir>vs2019\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2019\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <OutDir>vs2019\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2019\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <OutDir>vs2019\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2019\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <OutDir>vs2019\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2019\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n    <ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"..\\src\\pugixml.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2022.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{07CF01C0-B887-499D-AD9C-799CB6A9FE64}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>pugixml</RootNamespace>\r\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <OutDir>vs2022\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2022\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <OutDir>vs2022\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2022\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <OutDir>vs2022\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2022\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <OutDir>vs2022\\$(Platform)_$(Configuration)\\</OutDir>\r\n    <IntDir>vs2022\\$(Platform)_$(Configuration)\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n    <ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"..\\src\\pugixml.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>\r\n"
  },
  {
    "path": "third-party/pugixml/scripts/pugixml_vs2022_static.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{07CF01C0-B887-499D-AD9C-799CB6A9FE64}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>pugixml</RootNamespace>\r\n    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n    <WholeProgramOptimization>false</WholeProgramOptimization>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <OutDir>vs2022\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2022\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <OutDir>vs2022\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2022\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <OutDir>vs2022\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2022\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <OutDir>vs2022\\$(Platform)_$(Configuration)Static\\</OutDir>\r\n    <IntDir>vs2022\\$(Platform)_$(Configuration)Static\\</IntDir>\r\n    <TargetName>pugixml</TargetName>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <Optimization>Disabled</Optimization>\r\n      <PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <PrecompiledHeader>\r\n      </PrecompiledHeader>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>\r\n      <DebugInformationFormat>OldStyle</DebugInformationFormat>\r\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"..\\src\\pugiconfig.hpp\" />\r\n    <ClInclude Include=\"..\\src\\pugixml.hpp\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"..\\src\\pugixml.cpp\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>\r\n"
  },
  {
    "path": "third-party/pugixml/src/pugiconfig.hpp",
    "content": "/**\r\n * pugixml parser - version 1.14\r\n * --------------------------------------------------------\r\n * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)\r\n * Report bugs and download new versions at https://pugixml.org/\r\n *\r\n * This library is distributed under the MIT License. See notice at the end\r\n * of this file.\r\n *\r\n * This work is based on the pugxml parser, which is:\r\n * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)\r\n */\r\n\r\n#ifndef HEADER_PUGICONFIG_HPP\r\n#define HEADER_PUGICONFIG_HPP\r\n\r\n// Uncomment this to enable wchar_t mode\r\n// #define PUGIXML_WCHAR_MODE\r\n\r\n// Uncomment this to enable compact mode\r\n// #define PUGIXML_COMPACT\r\n\r\n// Uncomment this to disable XPath\r\n// #define PUGIXML_NO_XPATH\r\n\r\n// Uncomment this to disable STL\r\n// #define PUGIXML_NO_STL\r\n\r\n// Uncomment this to disable exceptions\r\n// #define PUGIXML_NO_EXCEPTIONS\r\n\r\n// Set this to control attributes for public classes/functions, i.e.:\r\n// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL\r\n// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL\r\n// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall\r\n// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead\r\n\r\n// Tune these constants to adjust memory-related behavior\r\n// #define PUGIXML_MEMORY_PAGE_SIZE 32768\r\n// #define PUGIXML_MEMORY_OUTPUT_STACK 10240\r\n// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096\r\n\r\n// Tune this constant to adjust max nesting for XPath queries\r\n// #define PUGIXML_XPATH_DEPTH_LIMIT 1024\r\n\r\n// Uncomment this to switch to header-only version\r\n// #define PUGIXML_HEADER_ONLY\r\n\r\n// Uncomment this to enable long long support\r\n// #define PUGIXML_HAS_LONG_LONG\r\n\r\n#endif\r\n\r\n/**\r\n * Copyright (c) 2006-2023 Arseny Kapoulkine\r\n *\r\n * Permission is hereby granted, free of charge, to any person\r\n * obtaining a copy of this software and associated documentation\r\n * files (the \"Software\"), to deal in the Software without\r\n * restriction, including without limitation the rights to use,\r\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n * copies of the Software, and to permit persons to whom the\r\n * Software is furnished to do so, subject to the following\r\n * conditions:\r\n *\r\n * The above copyright notice and this permission notice shall be\r\n * included in all copies or substantial portions of the Software.\r\n *\r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\r\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\r\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\r\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\r\n * OTHER DEALINGS IN THE SOFTWARE.\r\n */\r\n"
  },
  {
    "path": "third-party/pugixml/src/pugixml.cpp",
    "content": "/**\r\n * pugixml parser - version 1.14\r\n * --------------------------------------------------------\r\n * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)\r\n * Report bugs and download new versions at https://pugixml.org/\r\n *\r\n * This library is distributed under the MIT License. See notice at the end\r\n * of this file.\r\n *\r\n * This work is based on the pugxml parser, which is:\r\n * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)\r\n */\r\n\r\n#ifndef SOURCE_PUGIXML_CPP\r\n#define SOURCE_PUGIXML_CPP\r\n\r\n#include \"pugixml.hpp\"\r\n\r\n#include <stdlib.h>\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <assert.h>\r\n#include <limits.h>\r\n\r\n#ifdef PUGIXML_WCHAR_MODE\r\n#\tinclude <wchar.h>\r\n#endif\r\n\r\n#ifndef PUGIXML_NO_XPATH\r\n#\tinclude <math.h>\r\n#\tinclude <float.h>\r\n#endif\r\n\r\n#ifndef PUGIXML_NO_STL\r\n#\tinclude <istream>\r\n#\tinclude <ostream>\r\n#\tinclude <string>\r\n#endif\r\n\r\n// For placement new\r\n#include <new>\r\n\r\n// For load_file\r\n#if defined(__linux__) || defined(__APPLE__)\r\n#include <sys/stat.h>\r\n#endif\r\n\r\n#ifdef _MSC_VER\r\n#\tpragma warning(push)\r\n#\tpragma warning(disable: 4127) // conditional expression is constant\r\n#\tpragma warning(disable: 4324) // structure was padded due to __declspec(align())\r\n#\tpragma warning(disable: 4702) // unreachable code\r\n#\tpragma warning(disable: 4996) // this function or variable may be unsafe\r\n#endif\r\n\r\n#if defined(_MSC_VER) && defined(__c2__)\r\n#\tpragma clang diagnostic push\r\n#\tpragma clang diagnostic ignored \"-Wdeprecated\" // this function or variable may be unsafe\r\n#endif\r\n\r\n#ifdef __INTEL_COMPILER\r\n#\tpragma warning(disable: 177) // function was declared but never referenced\r\n#\tpragma warning(disable: 279) // controlling expression is constant\r\n#\tpragma warning(disable: 1478 1786) // function was declared \"deprecated\"\r\n#\tpragma warning(disable: 1684) // conversion from pointer to same-sized integral type\r\n#endif\r\n\r\n#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY)\r\n#\tpragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away\r\n#endif\r\n\r\n#ifdef __BORLANDC__\r\n#\tpragma option push\r\n#\tpragma warn -8008 // condition is always false\r\n#\tpragma warn -8066 // unreachable code\r\n#endif\r\n\r\n#ifdef __SNC__\r\n// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug\r\n#\tpragma diag_suppress=178 // function was declared but never referenced\r\n#\tpragma diag_suppress=237 // controlling expression is constant\r\n#endif\r\n\r\n#ifdef __TI_COMPILER_VERSION__\r\n#\tpragma diag_suppress 179 // function was declared but never referenced\r\n#endif\r\n\r\n// Inlining controls\r\n#if defined(_MSC_VER) && _MSC_VER >= 1300\r\n#\tdefine PUGI_IMPL_NO_INLINE __declspec(noinline)\r\n#elif defined(__GNUC__)\r\n#\tdefine PUGI_IMPL_NO_INLINE __attribute__((noinline))\r\n#else\r\n#\tdefine PUGI_IMPL_NO_INLINE\r\n#endif\r\n\r\n// Branch weight controls\r\n#if defined(__GNUC__) && !defined(__c2__)\r\n#\tdefine PUGI_IMPL_UNLIKELY(cond) __builtin_expect(cond, 0)\r\n#else\r\n#\tdefine PUGI_IMPL_UNLIKELY(cond) (cond)\r\n#endif\r\n\r\n// Simple static assertion\r\n#define PUGI_IMPL_STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }\r\n\r\n// Digital Mars C++ bug workaround for passing char loaded from memory via stack\r\n#ifdef __DMC__\r\n#\tdefine PUGI_IMPL_DMC_VOLATILE volatile\r\n#else\r\n#\tdefine PUGI_IMPL_DMC_VOLATILE\r\n#endif\r\n\r\n// Integer sanitizer workaround; we only apply this for clang since gcc8 has no_sanitize but not unsigned-integer-overflow and produces \"attribute directive ignored\" warnings\r\n#if defined(__clang__) && defined(__has_attribute)\r\n#\tif __has_attribute(no_sanitize)\r\n#\t\tdefine PUGI_IMPL_UNSIGNED_OVERFLOW __attribute__((no_sanitize(\"unsigned-integer-overflow\")))\r\n#\telse\r\n#\t\tdefine PUGI_IMPL_UNSIGNED_OVERFLOW\r\n#\tendif\r\n#else\r\n#\tdefine PUGI_IMPL_UNSIGNED_OVERFLOW\r\n#endif\r\n\r\n// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all)\r\n#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST)\r\nusing std::memcpy;\r\nusing std::memmove;\r\nusing std::memset;\r\n#endif\r\n\r\n// Old versions of GCC do not define ::malloc and ::free depending on header include order\r\n#if defined(__GNUC__) && (__GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 4))\r\nusing std::malloc;\r\nusing std::free;\r\n#endif\r\n\r\n// Some MinGW/GCC versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions from limits.h in some configurations\r\n#if defined(PUGIXML_HAS_LONG_LONG) && defined(__GNUC__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX)\r\n#\tdefine LLONG_MIN (-LLONG_MAX - 1LL)\r\n#\tdefine LLONG_MAX __LONG_LONG_MAX__\r\n#\tdefine ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL)\r\n#endif\r\n\r\n// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features\r\n#if defined(_MSC_VER) && !defined(__S3E__) && !defined(_WIN32_WCE)\r\n#\tdefine PUGI_IMPL_MSVC_CRT_VERSION _MSC_VER\r\n#elif defined(_WIN32_WCE)\r\n#\tdefine PUGI_IMPL_MSVC_CRT_VERSION 1310 // MSVC7.1\r\n#endif\r\n\r\n// Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size.\r\n#if __cplusplus >= 201103\r\n#\tdefine PUGI_IMPL_SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__)\r\n#elif defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400\r\n#\tdefine PUGI_IMPL_SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__)\r\n#elif defined(__APPLE__) && __clang_major__ >= 14 // Xcode 14 marks sprintf as deprecated while still using C++98 by default\r\n#\tdefine PUGI_IMPL_SNPRINTF(buf, fmt, arg1, arg2) snprintf(buf, sizeof(buf), fmt, arg1, arg2)\r\n#else\r\n#\tdefine PUGI_IMPL_SNPRINTF sprintf\r\n#endif\r\n\r\n// We put implementation details into an anonymous namespace in source mode, but have to keep it in non-anonymous namespace in header-only mode to prevent binary bloat.\r\n#ifdef PUGIXML_HEADER_ONLY\r\n#\tdefine PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl {\r\n#\tdefine PUGI_IMPL_NS_END } }\r\n#\tdefine PUGI_IMPL_FN inline\r\n#\tdefine PUGI_IMPL_FN_NO_INLINE inline\r\n#else\r\n#\tif defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces\r\n#\t\tdefine PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl {\r\n#\t\tdefine PUGI_IMPL_NS_END } }\r\n#\telse\r\n#\t\tdefine PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl { namespace {\r\n#\t\tdefine PUGI_IMPL_NS_END } } }\r\n#\tendif\r\n#\tdefine PUGI_IMPL_FN\r\n#\tdefine PUGI_IMPL_FN_NO_INLINE PUGI_IMPL_NO_INLINE\r\n#endif\r\n\r\n// uintptr_t\r\n#if (defined(_MSC_VER) && _MSC_VER < 1600) || (defined(__BORLANDC__) && __BORLANDC__ < 0x561)\r\nnamespace pugi\r\n{\r\n#\tifndef _UINTPTR_T_DEFINED\r\n\ttypedef size_t uintptr_t;\r\n#\tendif\r\n\r\n\ttypedef unsigned __int8 uint8_t;\r\n\ttypedef unsigned __int16 uint16_t;\r\n\ttypedef unsigned __int32 uint32_t;\r\n}\r\n#else\r\n#\tinclude <stdint.h>\r\n#endif\r\n\r\n// Memory allocation\r\nPUGI_IMPL_NS_BEGIN\r\n\tPUGI_IMPL_FN void* default_allocate(size_t size)\r\n\t{\r\n\t\treturn malloc(size);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void default_deallocate(void* ptr)\r\n\t{\r\n\t\tfree(ptr);\r\n\t}\r\n\r\n\ttemplate <typename T>\r\n\tstruct xml_memory_management_function_storage\r\n\t{\r\n\t\tstatic allocation_function allocate;\r\n\t\tstatic deallocation_function deallocate;\r\n\t};\r\n\r\n\t// Global allocation functions are stored in class statics so that in header mode linker deduplicates them\r\n\t// Without a template<> we'll get multiple definitions of the same static\r\n\ttemplate <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate;\r\n\ttemplate <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;\r\n\r\n\ttypedef xml_memory_management_function_storage<int> xml_memory;\r\nPUGI_IMPL_NS_END\r\n\r\n// String utilities\r\nPUGI_IMPL_NS_BEGIN\r\n\t// Get string length\r\n\tPUGI_IMPL_FN size_t strlength(const char_t* s)\r\n\t{\r\n\t\tassert(s);\r\n\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\treturn wcslen(s);\r\n\t#else\r\n\t\treturn strlen(s);\r\n\t#endif\r\n\t}\r\n\r\n\t// Compare two strings\r\n\tPUGI_IMPL_FN bool strequal(const char_t* src, const char_t* dst)\r\n\t{\r\n\t\tassert(src && dst);\r\n\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\treturn wcscmp(src, dst) == 0;\r\n\t#else\r\n\t\treturn strcmp(src, dst) == 0;\r\n\t#endif\r\n\t}\r\n\r\n\t// Compare lhs with [rhs_begin, rhs_end)\r\n\tPUGI_IMPL_FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count)\r\n\t{\r\n\t\tfor (size_t i = 0; i < count; ++i)\r\n\t\t\tif (lhs[i] != rhs[i])\r\n\t\t\t\treturn false;\r\n\r\n\t\treturn lhs[count] == 0;\r\n\t}\r\n\r\n\t// Get length of wide string, even if CRT lacks wide character support\r\n\tPUGI_IMPL_FN size_t strlength_wide(const wchar_t* s)\r\n\t{\r\n\t\tassert(s);\r\n\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\treturn wcslen(s);\r\n\t#else\r\n\t\tconst wchar_t* end = s;\r\n\t\twhile (*end) end++;\r\n\t\treturn static_cast<size_t>(end - s);\r\n\t#endif\r\n\t}\r\nPUGI_IMPL_NS_END\r\n\r\n// auto_ptr-like object for exception recovery\r\nPUGI_IMPL_NS_BEGIN\r\n\ttemplate <typename T> struct auto_deleter\r\n\t{\r\n\t\ttypedef void (*D)(T*);\r\n\r\n\t\tT* data;\r\n\t\tD deleter;\r\n\r\n\t\tauto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\t~auto_deleter()\r\n\t\t{\r\n\t\t\tif (data) deleter(data);\r\n\t\t}\r\n\r\n\t\tT* release()\r\n\t\t{\r\n\t\t\tT* result = data;\r\n\t\t\tdata = 0;\r\n\t\t\treturn result;\r\n\t\t}\r\n\t};\r\nPUGI_IMPL_NS_END\r\n\r\n#ifdef PUGIXML_COMPACT\r\nPUGI_IMPL_NS_BEGIN\r\n\tclass compact_hash_table\r\n\t{\r\n\tpublic:\r\n\t\tcompact_hash_table(): _items(0), _capacity(0), _count(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tvoid clear()\r\n\t\t{\r\n\t\t\tif (_items)\r\n\t\t\t{\r\n\t\t\t\txml_memory::deallocate(_items);\r\n\t\t\t\t_items = 0;\r\n\t\t\t\t_capacity = 0;\r\n\t\t\t\t_count = 0;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvoid* find(const void* key)\r\n\t\t{\r\n\t\t\tif (_capacity == 0) return 0;\r\n\r\n\t\t\titem_t* item = get_item(key);\r\n\t\t\tassert(item);\r\n\t\t\tassert(item->key == key || (item->key == 0 && item->value == 0));\r\n\r\n\t\t\treturn item->value;\r\n\t\t}\r\n\r\n\t\tvoid insert(const void* key, void* value)\r\n\t\t{\r\n\t\t\tassert(_capacity != 0 && _count < _capacity - _capacity / 4);\r\n\r\n\t\t\titem_t* item = get_item(key);\r\n\t\t\tassert(item);\r\n\r\n\t\t\tif (item->key == 0)\r\n\t\t\t{\r\n\t\t\t\t_count++;\r\n\t\t\t\titem->key = key;\r\n\t\t\t}\r\n\r\n\t\t\titem->value = value;\r\n\t\t}\r\n\r\n\t\tbool reserve(size_t extra = 16)\r\n\t\t{\r\n\t\t\tif (_count + extra >= _capacity - _capacity / 4)\r\n\t\t\t\treturn rehash(_count + extra);\r\n\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\tprivate:\r\n\t\tstruct item_t\r\n\t\t{\r\n\t\t\tconst void* key;\r\n\t\t\tvoid* value;\r\n\t\t};\r\n\r\n\t\titem_t* _items;\r\n\t\tsize_t _capacity;\r\n\r\n\t\tsize_t _count;\r\n\r\n\t\tbool rehash(size_t count);\r\n\r\n\t\titem_t* get_item(const void* key)\r\n\t\t{\r\n\t\t\tassert(key);\r\n\t\t\tassert(_capacity > 0);\r\n\r\n\t\t\tsize_t hashmod = _capacity - 1;\r\n\t\t\tsize_t bucket = hash(key) & hashmod;\r\n\r\n\t\t\tfor (size_t probe = 0; probe <= hashmod; ++probe)\r\n\t\t\t{\r\n\t\t\t\titem_t& probe_item = _items[bucket];\r\n\r\n\t\t\t\tif (probe_item.key == key || probe_item.key == 0)\r\n\t\t\t\t\treturn &probe_item;\r\n\r\n\t\t\t\t// hash collision, quadratic probing\r\n\t\t\t\tbucket = (bucket + probe + 1) & hashmod;\r\n\t\t\t}\r\n\r\n\t\t\tassert(false && \"Hash table is full\"); // unreachable\r\n\t\t\treturn 0;\r\n\t\t}\r\n\r\n\t\tstatic PUGI_IMPL_UNSIGNED_OVERFLOW unsigned int hash(const void* key)\r\n\t\t{\r\n\t\t\tunsigned int h = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(key) & 0xffffffff);\r\n\r\n\t\t\t// MurmurHash3 32-bit finalizer\r\n\t\t\th ^= h >> 16;\r\n\t\t\th *= 0x85ebca6bu;\r\n\t\t\th ^= h >> 13;\r\n\t\t\th *= 0xc2b2ae35u;\r\n\t\t\th ^= h >> 16;\r\n\r\n\t\t\treturn h;\r\n\t\t}\r\n\t};\r\n\r\n\tPUGI_IMPL_FN_NO_INLINE bool compact_hash_table::rehash(size_t count)\r\n\t{\r\n\t\tsize_t capacity = 32;\r\n\t\twhile (count >= capacity - capacity / 4)\r\n\t\t\tcapacity *= 2;\r\n\r\n\t\tcompact_hash_table rt;\r\n\t\trt._capacity = capacity;\r\n\t\trt._items = static_cast<item_t*>(xml_memory::allocate(sizeof(item_t) * capacity));\r\n\r\n\t\tif (!rt._items)\r\n\t\t\treturn false;\r\n\r\n\t\tmemset(rt._items, 0, sizeof(item_t) * capacity);\r\n\r\n\t\tfor (size_t i = 0; i < _capacity; ++i)\r\n\t\t\tif (_items[i].key)\r\n\t\t\t\trt.insert(_items[i].key, _items[i].value);\r\n\r\n\t\tif (_items)\r\n\t\t\txml_memory::deallocate(_items);\r\n\r\n\t\t_capacity = capacity;\r\n\t\t_items = rt._items;\r\n\r\n\t\tassert(_count == rt._count);\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\nPUGI_IMPL_NS_END\r\n#endif\r\n\r\nPUGI_IMPL_NS_BEGIN\r\n#ifdef PUGIXML_COMPACT\r\n\tstatic const uintptr_t xml_memory_block_alignment = 4;\r\n#else\r\n\tstatic const uintptr_t xml_memory_block_alignment = sizeof(void*);\r\n#endif\r\n\r\n\t// extra metadata bits\r\n\tstatic const uintptr_t xml_memory_page_contents_shared_mask = 64;\r\n\tstatic const uintptr_t xml_memory_page_name_allocated_mask = 32;\r\n\tstatic const uintptr_t xml_memory_page_value_allocated_mask = 16;\r\n\tstatic const uintptr_t xml_memory_page_type_mask = 15;\r\n\r\n\t// combined masks for string uniqueness\r\n\tstatic const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask;\r\n\tstatic const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask;\r\n\r\n#ifdef PUGIXML_COMPACT\r\n\t#define PUGI_IMPL_GETHEADER_IMPL(object, page, flags) // unused\r\n\t#define PUGI_IMPL_GETPAGE_IMPL(header) (header).get_page()\r\n#else\r\n\t#define PUGI_IMPL_GETHEADER_IMPL(object, page, flags) (((reinterpret_cast<char*>(object) - reinterpret_cast<char*>(page)) << 8) | (flags))\r\n\t// this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings\r\n\t#define PUGI_IMPL_GETPAGE_IMPL(header) static_cast<impl::xml_memory_page*>(const_cast<void*>(static_cast<const void*>(reinterpret_cast<const char*>(&header) - (header >> 8))))\r\n#endif\r\n\r\n\t#define PUGI_IMPL_GETPAGE(n) PUGI_IMPL_GETPAGE_IMPL((n)->header)\r\n\t#define PUGI_IMPL_NODETYPE(n) static_cast<xml_node_type>((n)->header & impl::xml_memory_page_type_mask)\r\n\r\n\tstruct xml_allocator;\r\n\r\n\tstruct xml_memory_page\r\n\t{\r\n\t\tstatic xml_memory_page* construct(void* memory)\r\n\t\t{\r\n\t\t\txml_memory_page* result = static_cast<xml_memory_page*>(memory);\r\n\r\n\t\t\tresult->allocator = 0;\r\n\t\t\tresult->prev = 0;\r\n\t\t\tresult->next = 0;\r\n\t\t\tresult->busy_size = 0;\r\n\t\t\tresult->freed_size = 0;\r\n\r\n\t\t#ifdef PUGIXML_COMPACT\r\n\t\t\tresult->compact_string_base = 0;\r\n\t\t\tresult->compact_shared_parent = 0;\r\n\t\t\tresult->compact_page_marker = 0;\r\n\t\t#endif\r\n\r\n\t\t\treturn result;\r\n\t\t}\r\n\r\n\t\txml_allocator* allocator;\r\n\r\n\t\txml_memory_page* prev;\r\n\t\txml_memory_page* next;\r\n\r\n\t\tsize_t busy_size;\r\n\t\tsize_t freed_size;\r\n\r\n\t#ifdef PUGIXML_COMPACT\r\n\t\tchar_t* compact_string_base;\r\n\t\tvoid* compact_shared_parent;\r\n\t\tuint32_t* compact_page_marker;\r\n\t#endif\r\n\t};\r\n\r\n\tstatic const size_t xml_memory_page_size =\r\n\t#ifdef PUGIXML_MEMORY_PAGE_SIZE\r\n\t\t(PUGIXML_MEMORY_PAGE_SIZE)\r\n\t#else\r\n\t\t32768\r\n\t#endif\r\n\t\t- sizeof(xml_memory_page);\r\n\r\n\tstruct xml_memory_string_header\r\n\t{\r\n\t\tuint16_t page_offset; // offset from page->data\r\n\t\tuint16_t full_size; // 0 if string occupies whole page\r\n\t};\r\n\r\n\tstruct xml_allocator\r\n\t{\r\n\t\txml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size)\r\n\t\t{\r\n\t\t#ifdef PUGIXML_COMPACT\r\n\t\t\t_hash = 0;\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\txml_memory_page* allocate_page(size_t data_size)\r\n\t\t{\r\n\t\t\tsize_t size = sizeof(xml_memory_page) + data_size;\r\n\r\n\t\t\t// allocate block with some alignment, leaving memory for worst-case padding\r\n\t\t\tvoid* memory = xml_memory::allocate(size);\r\n\t\t\tif (!memory) return 0;\r\n\r\n\t\t\t// prepare page structure\r\n\t\t\txml_memory_page* page = xml_memory_page::construct(memory);\r\n\t\t\tassert(page);\r\n\r\n\t\t\tassert(this == _root->allocator);\r\n\t\t\tpage->allocator = this;\r\n\r\n\t\t\treturn page;\r\n\t\t}\r\n\r\n\t\tstatic void deallocate_page(xml_memory_page* page)\r\n\t\t{\r\n\t\t\txml_memory::deallocate(page);\r\n\t\t}\r\n\r\n\t\tvoid* allocate_memory_oob(size_t size, xml_memory_page*& out_page);\r\n\r\n\t\tvoid* allocate_memory(size_t size, xml_memory_page*& out_page)\r\n\t\t{\r\n\t\t\tif (PUGI_IMPL_UNLIKELY(_busy_size + size > xml_memory_page_size))\r\n\t\t\t\treturn allocate_memory_oob(size, out_page);\r\n\r\n\t\t\tvoid* buf = reinterpret_cast<char*>(_root) + sizeof(xml_memory_page) + _busy_size;\r\n\r\n\t\t\t_busy_size += size;\r\n\r\n\t\t\tout_page = _root;\r\n\r\n\t\t\treturn buf;\r\n\t\t}\r\n\r\n\t#ifdef PUGIXML_COMPACT\r\n\t\tvoid* allocate_object(size_t size, xml_memory_page*& out_page)\r\n\t\t{\r\n\t\t\tvoid* result = allocate_memory(size + sizeof(uint32_t), out_page);\r\n\t\t\tif (!result) return 0;\r\n\r\n\t\t\t// adjust for marker\r\n\t\t\tptrdiff_t offset = static_cast<char*>(result) - reinterpret_cast<char*>(out_page->compact_page_marker);\r\n\r\n\t\t\tif (PUGI_IMPL_UNLIKELY(static_cast<uintptr_t>(offset) >= 256 * xml_memory_block_alignment))\r\n\t\t\t{\r\n\t\t\t\t// insert new marker\r\n\t\t\t\tuint32_t* marker = static_cast<uint32_t*>(result);\r\n\r\n\t\t\t\t*marker = static_cast<uint32_t>(reinterpret_cast<char*>(marker) - reinterpret_cast<char*>(out_page));\r\n\t\t\t\tout_page->compact_page_marker = marker;\r\n\r\n\t\t\t\t// since we don't reuse the page space until we reallocate it, we can just pretend that we freed the marker block\r\n\t\t\t\t// this will make sure deallocate_memory correctly tracks the size\r\n\t\t\t\tout_page->freed_size += sizeof(uint32_t);\r\n\r\n\t\t\t\treturn marker + 1;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// roll back uint32_t part\r\n\t\t\t\t_busy_size -= sizeof(uint32_t);\r\n\r\n\t\t\t\treturn result;\r\n\t\t\t}\r\n\t\t}\r\n\t#else\r\n\t\tvoid* allocate_object(size_t size, xml_memory_page*& out_page)\r\n\t\t{\r\n\t\t\treturn allocate_memory(size, out_page);\r\n\t\t}\r\n\t#endif\r\n\r\n\t\tvoid deallocate_memory(void* ptr, size_t size, xml_memory_page* page)\r\n\t\t{\r\n\t\t\tif (page == _root) page->busy_size = _busy_size;\r\n\r\n\t\t\tassert(ptr >= reinterpret_cast<char*>(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast<char*>(page) + sizeof(xml_memory_page) + page->busy_size);\r\n\t\t\t(void)!ptr;\r\n\r\n\t\t\tpage->freed_size += size;\r\n\t\t\tassert(page->freed_size <= page->busy_size);\r\n\r\n\t\t\tif (page->freed_size == page->busy_size)\r\n\t\t\t{\r\n\t\t\t\tif (page->next == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tassert(_root == page);\r\n\r\n\t\t\t\t\t// top page freed, just reset sizes\r\n\t\t\t\t\tpage->busy_size = 0;\r\n\t\t\t\t\tpage->freed_size = 0;\r\n\r\n\t\t\t\t#ifdef PUGIXML_COMPACT\r\n\t\t\t\t\t// reset compact state to maximize efficiency\r\n\t\t\t\t\tpage->compact_string_base = 0;\r\n\t\t\t\t\tpage->compact_shared_parent = 0;\r\n\t\t\t\t\tpage->compact_page_marker = 0;\r\n\t\t\t\t#endif\r\n\r\n\t\t\t\t\t_busy_size = 0;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tassert(_root != page);\r\n\t\t\t\t\tassert(page->prev);\r\n\r\n\t\t\t\t\t// remove from the list\r\n\t\t\t\t\tpage->prev->next = page->next;\r\n\t\t\t\t\tpage->next->prev = page->prev;\r\n\r\n\t\t\t\t\t// deallocate\r\n\t\t\t\t\tdeallocate_page(page);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tchar_t* allocate_string(size_t length)\r\n\t\t{\r\n\t\t\tstatic const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment;\r\n\r\n\t\t\tPUGI_IMPL_STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset);\r\n\r\n\t\t\t// allocate memory for string and header block\r\n\t\t\tsize_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t);\r\n\r\n\t\t\t// round size up to block alignment boundary\r\n\t\t\tsize_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1);\r\n\r\n\t\t\txml_memory_page* page;\r\n\t\t\txml_memory_string_header* header = static_cast<xml_memory_string_header*>(allocate_memory(full_size, page));\r\n\r\n\t\t\tif (!header) return 0;\r\n\r\n\t\t\t// setup header\r\n\t\t\tptrdiff_t page_offset = reinterpret_cast<char*>(header) - reinterpret_cast<char*>(page) - sizeof(xml_memory_page);\r\n\r\n\t\t\tassert(page_offset % xml_memory_block_alignment == 0);\r\n\t\t\tassert(page_offset >= 0 && static_cast<size_t>(page_offset) < max_encoded_offset);\r\n\t\t\theader->page_offset = static_cast<uint16_t>(static_cast<size_t>(page_offset) / xml_memory_block_alignment);\r\n\r\n\t\t\t// full_size == 0 for large strings that occupy the whole page\r\n\t\t\tassert(full_size % xml_memory_block_alignment == 0);\r\n\t\t\tassert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0));\r\n\t\t\theader->full_size = static_cast<uint16_t>(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 0);\r\n\r\n\t\t\t// round-trip through void* to avoid 'cast increases required alignment of target type' warning\r\n\t\t\t// header is guaranteed a pointer-sized alignment, which should be enough for char_t\r\n\t\t\treturn static_cast<char_t*>(static_cast<void*>(header + 1));\r\n\t\t}\r\n\r\n\t\tvoid deallocate_string(char_t* string)\r\n\t\t{\r\n\t\t\t// this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings\r\n\t\t\t// we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string\r\n\r\n\t\t\t// get header\r\n\t\t\txml_memory_string_header* header = static_cast<xml_memory_string_header*>(static_cast<void*>(string)) - 1;\r\n\t\t\tassert(header);\r\n\r\n\t\t\t// deallocate\r\n\t\t\tsize_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment;\r\n\t\t\txml_memory_page* page = reinterpret_cast<xml_memory_page*>(static_cast<void*>(reinterpret_cast<char*>(header) - page_offset));\r\n\r\n\t\t\t// if full_size == 0 then this string occupies the whole page\r\n\t\t\tsize_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment;\r\n\r\n\t\t\tdeallocate_memory(header, full_size, page);\r\n\t\t}\r\n\r\n\t\tbool reserve()\r\n\t\t{\r\n\t\t#ifdef PUGIXML_COMPACT\r\n\t\t\treturn _hash->reserve();\r\n\t\t#else\r\n\t\t\treturn true;\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\txml_memory_page* _root;\r\n\t\tsize_t _busy_size;\r\n\r\n\t#ifdef PUGIXML_COMPACT\r\n\t\tcompact_hash_table* _hash;\r\n\t#endif\r\n\t};\r\n\r\n\tPUGI_IMPL_FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page)\r\n\t{\r\n\t\tconst size_t large_allocation_threshold = xml_memory_page_size / 4;\r\n\r\n\t\txml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size);\r\n\t\tout_page = page;\r\n\r\n\t\tif (!page) return 0;\r\n\r\n\t\tif (size <= large_allocation_threshold)\r\n\t\t{\r\n\t\t\t_root->busy_size = _busy_size;\r\n\r\n\t\t\t// insert page at the end of linked list\r\n\t\t\tpage->prev = _root;\r\n\t\t\t_root->next = page;\r\n\t\t\t_root = page;\r\n\r\n\t\t\t_busy_size = size;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// insert page before the end of linked list, so that it is deleted as soon as possible\r\n\t\t\t// the last page is not deleted even if it's empty (see deallocate_memory)\r\n\t\t\tassert(_root->prev);\r\n\r\n\t\t\tpage->prev = _root->prev;\r\n\t\t\tpage->next = _root;\r\n\r\n\t\t\t_root->prev->next = page;\r\n\t\t\t_root->prev = page;\r\n\r\n\t\t\tpage->busy_size = size;\r\n\t\t}\r\n\r\n\t\treturn reinterpret_cast<char*>(page) + sizeof(xml_memory_page);\r\n\t}\r\nPUGI_IMPL_NS_END\r\n\r\n#ifdef PUGIXML_COMPACT\r\nPUGI_IMPL_NS_BEGIN\r\n\tstatic const uintptr_t compact_alignment_log2 = 2;\r\n\tstatic const uintptr_t compact_alignment = 1 << compact_alignment_log2;\r\n\r\n\tclass compact_header\r\n\t{\r\n\tpublic:\r\n\t\tcompact_header(xml_memory_page* page, unsigned int flags)\r\n\t\t{\r\n\t\t\tPUGI_IMPL_STATIC_ASSERT(xml_memory_block_alignment == compact_alignment);\r\n\r\n\t\t\tptrdiff_t offset = (reinterpret_cast<char*>(this) - reinterpret_cast<char*>(page->compact_page_marker));\r\n\t\t\tassert(offset % compact_alignment == 0 && static_cast<uintptr_t>(offset) < 256 * compact_alignment);\r\n\r\n\t\t\t_page = static_cast<unsigned char>(offset >> compact_alignment_log2);\r\n\t\t\t_flags = static_cast<unsigned char>(flags);\r\n\t\t}\r\n\r\n\t\tvoid operator&=(uintptr_t mod)\r\n\t\t{\r\n\t\t\t_flags &= static_cast<unsigned char>(mod);\r\n\t\t}\r\n\r\n\t\tvoid operator|=(uintptr_t mod)\r\n\t\t{\r\n\t\t\t_flags |= static_cast<unsigned char>(mod);\r\n\t\t}\r\n\r\n\t\tuintptr_t operator&(uintptr_t mod) const\r\n\t\t{\r\n\t\t\treturn _flags & mod;\r\n\t\t}\r\n\r\n\t\txml_memory_page* get_page() const\r\n\t\t{\r\n\t\t\t// round-trip through void* to silence 'cast increases required alignment of target type' warnings\r\n\t\t\tconst char* page_marker = reinterpret_cast<const char*>(this) - (_page << compact_alignment_log2);\r\n\t\t\tconst char* page = page_marker - *reinterpret_cast<const uint32_t*>(static_cast<const void*>(page_marker));\r\n\r\n\t\t\treturn const_cast<xml_memory_page*>(reinterpret_cast<const xml_memory_page*>(static_cast<const void*>(page)));\r\n\t\t}\r\n\r\n\tprivate:\r\n\t\tunsigned char _page;\r\n\t\tunsigned char _flags;\r\n\t};\r\n\r\n\tPUGI_IMPL_FN xml_memory_page* compact_get_page(const void* object, int header_offset)\r\n\t{\r\n\t\tconst compact_header* header = reinterpret_cast<const compact_header*>(static_cast<const char*>(object) - header_offset);\r\n\r\n\t\treturn header->get_page();\r\n\t}\r\n\r\n\ttemplate <int header_offset, typename T> PUGI_IMPL_FN_NO_INLINE T* compact_get_value(const void* object)\r\n\t{\r\n\t\treturn static_cast<T*>(compact_get_page(object, header_offset)->allocator->_hash->find(object));\r\n\t}\r\n\r\n\ttemplate <int header_offset, typename T> PUGI_IMPL_FN_NO_INLINE void compact_set_value(const void* object, T* value)\r\n\t{\r\n\t\tcompact_get_page(object, header_offset)->allocator->_hash->insert(object, value);\r\n\t}\r\n\r\n\ttemplate <typename T, int header_offset, int start = -126> class compact_pointer\r\n\t{\r\n\tpublic:\r\n\t\tcompact_pointer(): _data(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tvoid operator=(const compact_pointer& rhs)\r\n\t\t{\r\n\t\t\t*this = rhs + 0;\r\n\t\t}\r\n\r\n\t\tvoid operator=(T* value)\r\n\t\t{\r\n\t\t\tif (value)\r\n\t\t\t{\r\n\t\t\t\t// value is guaranteed to be compact-aligned; 'this' is not\r\n\t\t\t\t// our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)\r\n\t\t\t\t// so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to\r\n\t\t\t\t// compensate for arithmetic shift rounding for negative values\r\n\t\t\t\tptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);\r\n\t\t\t\tptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start;\r\n\r\n\t\t\t\tif (static_cast<uintptr_t>(offset) <= 253)\r\n\t\t\t\t\t_data = static_cast<unsigned char>(offset + 1);\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tcompact_set_value<header_offset>(this, value);\r\n\r\n\t\t\t\t\t_data = 255;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\t_data = 0;\r\n\t\t}\r\n\r\n\t\toperator T*() const\r\n\t\t{\r\n\t\t\tif (_data)\r\n\t\t\t{\r\n\t\t\t\tif (_data < 255)\r\n\t\t\t\t{\r\n\t\t\t\t\tuintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);\r\n\r\n\t\t\t\t\treturn reinterpret_cast<T*>(base + (_data - 1 + start) * compact_alignment);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t\treturn compact_get_value<header_offset, T>(this);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\treturn 0;\r\n\t\t}\r\n\r\n\t\tT* operator->() const\r\n\t\t{\r\n\t\t\treturn *this;\r\n\t\t}\r\n\r\n\tprivate:\r\n\t\tunsigned char _data;\r\n\t};\r\n\r\n\ttemplate <typename T, int header_offset> class compact_pointer_parent\r\n\t{\r\n\tpublic:\r\n\t\tcompact_pointer_parent(): _data(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tvoid operator=(const compact_pointer_parent& rhs)\r\n\t\t{\r\n\t\t\t*this = rhs + 0;\r\n\t\t}\r\n\r\n\t\tvoid operator=(T* value)\r\n\t\t{\r\n\t\t\tif (value)\r\n\t\t\t{\r\n\t\t\t\t// value is guaranteed to be compact-aligned; 'this' is not\r\n\t\t\t\t// our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)\r\n\t\t\t\t// so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to\r\n\t\t\t\t// compensate for arithmetic shift behavior for negative values\r\n\t\t\t\tptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);\r\n\t\t\t\tptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533;\r\n\r\n\t\t\t\tif (static_cast<uintptr_t>(offset) <= 65533)\r\n\t\t\t\t{\r\n\t\t\t\t\t_data = static_cast<unsigned short>(offset + 1);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\txml_memory_page* page = compact_get_page(this, header_offset);\r\n\r\n\t\t\t\t\tif (PUGI_IMPL_UNLIKELY(page->compact_shared_parent == 0))\r\n\t\t\t\t\t\tpage->compact_shared_parent = value;\r\n\r\n\t\t\t\t\tif (page->compact_shared_parent == value)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t_data = 65534;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcompact_set_value<header_offset>(this, value);\r\n\r\n\t\t\t\t\t\t_data = 65535;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t_data = 0;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\toperator T*() const\r\n\t\t{\r\n\t\t\tif (_data)\r\n\t\t\t{\r\n\t\t\t\tif (_data < 65534)\r\n\t\t\t\t{\r\n\t\t\t\t\tuintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);\r\n\r\n\t\t\t\t\treturn reinterpret_cast<T*>(base + (_data - 1 - 65533) * compact_alignment);\r\n\t\t\t\t}\r\n\t\t\t\telse if (_data == 65534)\r\n\t\t\t\t\treturn static_cast<T*>(compact_get_page(this, header_offset)->compact_shared_parent);\r\n\t\t\t\telse\r\n\t\t\t\t\treturn compact_get_value<header_offset, T>(this);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\treturn 0;\r\n\t\t}\r\n\r\n\t\tT* operator->() const\r\n\t\t{\r\n\t\t\treturn *this;\r\n\t\t}\r\n\r\n\tprivate:\r\n\t\tuint16_t _data;\r\n\t};\r\n\r\n\ttemplate <int header_offset, int base_offset> class compact_string\r\n\t{\r\n\tpublic:\r\n\t\tcompact_string(): _data(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tvoid operator=(const compact_string& rhs)\r\n\t\t{\r\n\t\t\t*this = rhs + 0;\r\n\t\t}\r\n\r\n\t\tvoid operator=(char_t* value)\r\n\t\t{\r\n\t\t\tif (value)\r\n\t\t\t{\r\n\t\t\t\txml_memory_page* page = compact_get_page(this, header_offset);\r\n\r\n\t\t\t\tif (PUGI_IMPL_UNLIKELY(page->compact_string_base == 0))\r\n\t\t\t\t\tpage->compact_string_base = value;\r\n\r\n\t\t\t\tptrdiff_t offset = value - page->compact_string_base;\r\n\r\n\t\t\t\tif (static_cast<uintptr_t>(offset) < (65535 << 7))\r\n\t\t\t\t{\r\n\t\t\t\t\t// round-trip through void* to silence 'cast increases required alignment of target type' warnings\r\n\t\t\t\t\tuint16_t* base = reinterpret_cast<uint16_t*>(static_cast<void*>(reinterpret_cast<char*>(this) - base_offset));\r\n\r\n\t\t\t\t\tif (*base == 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t*base = static_cast<uint16_t>((offset >> 7) + 1);\r\n\t\t\t\t\t\t_data = static_cast<unsigned char>((offset & 127) + 1);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tptrdiff_t remainder = offset - ((*base - 1) << 7);\r\n\r\n\t\t\t\t\t\tif (static_cast<uintptr_t>(remainder) <= 253)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t_data = static_cast<unsigned char>(remainder + 1);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcompact_set_value<header_offset>(this, value);\r\n\r\n\t\t\t\t\t\t\t_data = 255;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tcompact_set_value<header_offset>(this, value);\r\n\r\n\t\t\t\t\t_data = 255;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t_data = 0;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\toperator char_t*() const\r\n\t\t{\r\n\t\t\tif (_data)\r\n\t\t\t{\r\n\t\t\t\tif (_data < 255)\r\n\t\t\t\t{\r\n\t\t\t\t\txml_memory_page* page = compact_get_page(this, header_offset);\r\n\r\n\t\t\t\t\t// round-trip through void* to silence 'cast increases required alignment of target type' warnings\r\n\t\t\t\t\tconst uint16_t* base = reinterpret_cast<const uint16_t*>(static_cast<const void*>(reinterpret_cast<const char*>(this) - base_offset));\r\n\t\t\t\t\tassert(*base);\r\n\r\n\t\t\t\t\tptrdiff_t offset = ((*base - 1) << 7) + (_data - 1);\r\n\r\n\t\t\t\t\treturn page->compact_string_base + offset;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\treturn compact_get_value<header_offset, char_t>(this);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\treturn 0;\r\n\t\t}\r\n\r\n\tprivate:\r\n\t\tunsigned char _data;\r\n\t};\r\nPUGI_IMPL_NS_END\r\n#endif\r\n\r\n#ifdef PUGIXML_COMPACT\r\nnamespace pugi\r\n{\r\n\tstruct xml_attribute_struct\r\n\t{\r\n\t\txml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0)\r\n\t\t{\r\n\t\t\tPUGI_IMPL_STATIC_ASSERT(sizeof(xml_attribute_struct) == 8);\r\n\t\t}\r\n\r\n\t\timpl::compact_header header;\r\n\r\n\t\tuint16_t namevalue_base;\r\n\r\n\t\timpl::compact_string<4, 2> name;\r\n\t\timpl::compact_string<5, 3> value;\r\n\r\n\t\timpl::compact_pointer<xml_attribute_struct, 6> prev_attribute_c;\r\n\t\timpl::compact_pointer<xml_attribute_struct, 7, 0> next_attribute;\r\n\t};\r\n\r\n\tstruct xml_node_struct\r\n\t{\r\n\t\txml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type), namevalue_base(0)\r\n\t\t{\r\n\t\t\tPUGI_IMPL_STATIC_ASSERT(sizeof(xml_node_struct) == 12);\r\n\t\t}\r\n\r\n\t\timpl::compact_header header;\r\n\r\n\t\tuint16_t namevalue_base;\r\n\r\n\t\timpl::compact_string<4, 2> name;\r\n\t\timpl::compact_string<5, 3> value;\r\n\r\n\t\timpl::compact_pointer_parent<xml_node_struct, 6> parent;\r\n\r\n\t\timpl::compact_pointer<xml_node_struct, 8, 0> first_child;\r\n\r\n\t\timpl::compact_pointer<xml_node_struct,  9>    prev_sibling_c;\r\n\t\timpl::compact_pointer<xml_node_struct, 10, 0> next_sibling;\r\n\r\n\t\timpl::compact_pointer<xml_attribute_struct, 11, 0> first_attribute;\r\n\t};\r\n}\r\n#else\r\nnamespace pugi\r\n{\r\n\tstruct xml_attribute_struct\r\n\t{\r\n\t\txml_attribute_struct(impl::xml_memory_page* page): name(0), value(0), prev_attribute_c(0), next_attribute(0)\r\n\t\t{\r\n\t\t\theader = PUGI_IMPL_GETHEADER_IMPL(this, page, 0);\r\n\t\t}\r\n\r\n\t\tuintptr_t header;\r\n\r\n\t\tchar_t*\tname;\r\n\t\tchar_t*\tvalue;\r\n\r\n\t\txml_attribute_struct* prev_attribute_c;\r\n\t\txml_attribute_struct* next_attribute;\r\n\t};\r\n\r\n\tstruct xml_node_struct\r\n\t{\r\n\t\txml_node_struct(impl::xml_memory_page* page, xml_node_type type): name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)\r\n\t\t{\r\n\t\t\theader = PUGI_IMPL_GETHEADER_IMPL(this, page, type);\r\n\t\t}\r\n\r\n\t\tuintptr_t header;\r\n\r\n\t\tchar_t* name;\r\n\t\tchar_t* value;\r\n\r\n\t\txml_node_struct* parent;\r\n\r\n\t\txml_node_struct* first_child;\r\n\r\n\t\txml_node_struct* prev_sibling_c;\r\n\t\txml_node_struct* next_sibling;\r\n\r\n\t\txml_attribute_struct* first_attribute;\r\n\t};\r\n}\r\n#endif\r\n\r\nPUGI_IMPL_NS_BEGIN\r\n\tstruct xml_extra_buffer\r\n\t{\r\n\t\tchar_t* buffer;\r\n\t\txml_extra_buffer* next;\r\n\t};\r\n\r\n\tstruct xml_document_struct: public xml_node_struct, public xml_allocator\r\n\t{\r\n\t\txml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tconst char_t* buffer;\r\n\r\n\t\txml_extra_buffer* extra_buffers;\r\n\r\n\t#ifdef PUGIXML_COMPACT\r\n\t\tcompact_hash_table hash;\r\n\t#endif\r\n\t};\r\n\r\n\ttemplate <typename Object> inline xml_allocator& get_allocator(const Object* object)\r\n\t{\r\n\t\tassert(object);\r\n\r\n\t\treturn *PUGI_IMPL_GETPAGE(object)->allocator;\r\n\t}\r\n\r\n\ttemplate <typename Object> inline xml_document_struct& get_document(const Object* object)\r\n\t{\r\n\t\tassert(object);\r\n\r\n\t\treturn *static_cast<xml_document_struct*>(PUGI_IMPL_GETPAGE(object)->allocator);\r\n\t}\r\nPUGI_IMPL_NS_END\r\n\r\n// Low-level DOM operations\r\nPUGI_IMPL_NS_BEGIN\r\n\tinline xml_attribute_struct* allocate_attribute(xml_allocator& alloc)\r\n\t{\r\n\t\txml_memory_page* page;\r\n\t\tvoid* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page);\r\n\t\tif (!memory) return 0;\r\n\r\n\t\treturn new (memory) xml_attribute_struct(page);\r\n\t}\r\n\r\n\tinline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type)\r\n\t{\r\n\t\txml_memory_page* page;\r\n\t\tvoid* memory = alloc.allocate_object(sizeof(xml_node_struct), page);\r\n\t\tif (!memory) return 0;\r\n\r\n\t\treturn new (memory) xml_node_struct(page, type);\r\n\t}\r\n\r\n\tinline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc)\r\n\t{\r\n\t\tif (a->header & impl::xml_memory_page_name_allocated_mask)\r\n\t\t\talloc.deallocate_string(a->name);\r\n\r\n\t\tif (a->header & impl::xml_memory_page_value_allocated_mask)\r\n\t\t\talloc.deallocate_string(a->value);\r\n\r\n\t\talloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI_IMPL_GETPAGE(a));\r\n\t}\r\n\r\n\tinline void destroy_node(xml_node_struct* n, xml_allocator& alloc)\r\n\t{\r\n\t\tif (n->header & impl::xml_memory_page_name_allocated_mask)\r\n\t\t\talloc.deallocate_string(n->name);\r\n\r\n\t\tif (n->header & impl::xml_memory_page_value_allocated_mask)\r\n\t\t\talloc.deallocate_string(n->value);\r\n\r\n\t\tfor (xml_attribute_struct* attr = n->first_attribute; attr; )\r\n\t\t{\r\n\t\t\txml_attribute_struct* next = attr->next_attribute;\r\n\r\n\t\t\tdestroy_attribute(attr, alloc);\r\n\r\n\t\t\tattr = next;\r\n\t\t}\r\n\r\n\t\tfor (xml_node_struct* child = n->first_child; child; )\r\n\t\t{\r\n\t\t\txml_node_struct* next = child->next_sibling;\r\n\r\n\t\t\tdestroy_node(child, alloc);\r\n\r\n\t\t\tchild = next;\r\n\t\t}\r\n\r\n\t\talloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI_IMPL_GETPAGE(n));\r\n\t}\r\n\r\n\tinline void append_node(xml_node_struct* child, xml_node_struct* node)\r\n\t{\r\n\t\tchild->parent = node;\r\n\r\n\t\txml_node_struct* head = node->first_child;\r\n\r\n\t\tif (head)\r\n\t\t{\r\n\t\t\txml_node_struct* tail = head->prev_sibling_c;\r\n\r\n\t\t\ttail->next_sibling = child;\r\n\t\t\tchild->prev_sibling_c = tail;\r\n\t\t\thead->prev_sibling_c = child;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tnode->first_child = child;\r\n\t\t\tchild->prev_sibling_c = child;\r\n\t\t}\r\n\t}\r\n\r\n\tinline void prepend_node(xml_node_struct* child, xml_node_struct* node)\r\n\t{\r\n\t\tchild->parent = node;\r\n\r\n\t\txml_node_struct* head = node->first_child;\r\n\r\n\t\tif (head)\r\n\t\t{\r\n\t\t\tchild->prev_sibling_c = head->prev_sibling_c;\r\n\t\t\thead->prev_sibling_c = child;\r\n\t\t}\r\n\t\telse\r\n\t\t\tchild->prev_sibling_c = child;\r\n\r\n\t\tchild->next_sibling = head;\r\n\t\tnode->first_child = child;\r\n\t}\r\n\r\n\tinline void insert_node_after(xml_node_struct* child, xml_node_struct* node)\r\n\t{\r\n\t\txml_node_struct* parent = node->parent;\r\n\r\n\t\tchild->parent = parent;\r\n\r\n\t\txml_node_struct* next = node->next_sibling;\r\n\r\n\t\tif (next)\r\n\t\t\tnext->prev_sibling_c = child;\r\n\t\telse\r\n\t\t\tparent->first_child->prev_sibling_c = child;\r\n\r\n\t\tchild->next_sibling = next;\r\n\t\tchild->prev_sibling_c = node;\r\n\r\n\t\tnode->next_sibling = child;\r\n\t}\r\n\r\n\tinline void insert_node_before(xml_node_struct* child, xml_node_struct* node)\r\n\t{\r\n\t\txml_node_struct* parent = node->parent;\r\n\r\n\t\tchild->parent = parent;\r\n\r\n\t\txml_node_struct* prev = node->prev_sibling_c;\r\n\r\n\t\tif (prev->next_sibling)\r\n\t\t\tprev->next_sibling = child;\r\n\t\telse\r\n\t\t\tparent->first_child = child;\r\n\r\n\t\tchild->prev_sibling_c = prev;\r\n\t\tchild->next_sibling = node;\r\n\r\n\t\tnode->prev_sibling_c = child;\r\n\t}\r\n\r\n\tinline void remove_node(xml_node_struct* node)\r\n\t{\r\n\t\txml_node_struct* parent = node->parent;\r\n\r\n\t\txml_node_struct* next = node->next_sibling;\r\n\t\txml_node_struct* prev = node->prev_sibling_c;\r\n\r\n\t\tif (next)\r\n\t\t\tnext->prev_sibling_c = prev;\r\n\t\telse\r\n\t\t\tparent->first_child->prev_sibling_c = prev;\r\n\r\n\t\tif (prev->next_sibling)\r\n\t\t\tprev->next_sibling = next;\r\n\t\telse\r\n\t\t\tparent->first_child = next;\r\n\r\n\t\tnode->parent = 0;\r\n\t\tnode->prev_sibling_c = 0;\r\n\t\tnode->next_sibling = 0;\r\n\t}\r\n\r\n\tinline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node)\r\n\t{\r\n\t\txml_attribute_struct* head = node->first_attribute;\r\n\r\n\t\tif (head)\r\n\t\t{\r\n\t\t\txml_attribute_struct* tail = head->prev_attribute_c;\r\n\r\n\t\t\ttail->next_attribute = attr;\r\n\t\t\tattr->prev_attribute_c = tail;\r\n\t\t\thead->prev_attribute_c = attr;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tnode->first_attribute = attr;\r\n\t\t\tattr->prev_attribute_c = attr;\r\n\t\t}\r\n\t}\r\n\r\n\tinline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node)\r\n\t{\r\n\t\txml_attribute_struct* head = node->first_attribute;\r\n\r\n\t\tif (head)\r\n\t\t{\r\n\t\t\tattr->prev_attribute_c = head->prev_attribute_c;\r\n\t\t\thead->prev_attribute_c = attr;\r\n\t\t}\r\n\t\telse\r\n\t\t\tattr->prev_attribute_c = attr;\r\n\r\n\t\tattr->next_attribute = head;\r\n\t\tnode->first_attribute = attr;\r\n\t}\r\n\r\n\tinline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)\r\n\t{\r\n\t\txml_attribute_struct* next = place->next_attribute;\r\n\r\n\t\tif (next)\r\n\t\t\tnext->prev_attribute_c = attr;\r\n\t\telse\r\n\t\t\tnode->first_attribute->prev_attribute_c = attr;\r\n\r\n\t\tattr->next_attribute = next;\r\n\t\tattr->prev_attribute_c = place;\r\n\t\tplace->next_attribute = attr;\r\n\t}\r\n\r\n\tinline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)\r\n\t{\r\n\t\txml_attribute_struct* prev = place->prev_attribute_c;\r\n\r\n\t\tif (prev->next_attribute)\r\n\t\t\tprev->next_attribute = attr;\r\n\t\telse\r\n\t\t\tnode->first_attribute = attr;\r\n\r\n\t\tattr->prev_attribute_c = prev;\r\n\t\tattr->next_attribute = place;\r\n\t\tplace->prev_attribute_c = attr;\r\n\t}\r\n\r\n\tinline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node)\r\n\t{\r\n\t\txml_attribute_struct* next = attr->next_attribute;\r\n\t\txml_attribute_struct* prev = attr->prev_attribute_c;\r\n\r\n\t\tif (next)\r\n\t\t\tnext->prev_attribute_c = prev;\r\n\t\telse\r\n\t\t\tnode->first_attribute->prev_attribute_c = prev;\r\n\r\n\t\tif (prev->next_attribute)\r\n\t\t\tprev->next_attribute = next;\r\n\t\telse\r\n\t\t\tnode->first_attribute = next;\r\n\r\n\t\tattr->prev_attribute_c = 0;\r\n\t\tattr->next_attribute = 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)\r\n\t{\r\n\t\tif (!alloc.reserve()) return 0;\r\n\r\n\t\txml_node_struct* child = allocate_node(alloc, type);\r\n\t\tif (!child) return 0;\r\n\r\n\t\tappend_node(child, node);\r\n\r\n\t\treturn child;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc)\r\n\t{\r\n\t\tif (!alloc.reserve()) return 0;\r\n\r\n\t\txml_attribute_struct* attr = allocate_attribute(alloc);\r\n\t\tif (!attr) return 0;\r\n\r\n\t\tappend_attribute(attr, node);\r\n\r\n\t\treturn attr;\r\n\t}\r\nPUGI_IMPL_NS_END\r\n\r\n// Helper classes for code generation\r\nPUGI_IMPL_NS_BEGIN\r\n\tstruct opt_false\r\n\t{\r\n\t\tenum { value = 0 };\r\n\t};\r\n\r\n\tstruct opt_true\r\n\t{\r\n\t\tenum { value = 1 };\r\n\t};\r\nPUGI_IMPL_NS_END\r\n\r\n// Unicode utilities\r\nPUGI_IMPL_NS_BEGIN\r\n\tinline uint16_t endian_swap(uint16_t value)\r\n\t{\r\n\t\treturn static_cast<uint16_t>(((value & 0xff) << 8) | (value >> 8));\r\n\t}\r\n\r\n\tinline uint32_t endian_swap(uint32_t value)\r\n\t{\r\n\t\treturn ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24);\r\n\t}\r\n\r\n\tstruct utf8_counter\r\n\t{\r\n\t\ttypedef size_t value_type;\r\n\r\n\t\tstatic value_type low(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\t// U+0000..U+007F\r\n\t\t\tif (ch < 0x80) return result + 1;\r\n\t\t\t// U+0080..U+07FF\r\n\t\t\telse if (ch < 0x800) return result + 2;\r\n\t\t\t// U+0800..U+FFFF\r\n\t\t\telse return result + 3;\r\n\t\t}\r\n\r\n\t\tstatic value_type high(value_type result, uint32_t)\r\n\t\t{\r\n\t\t\t// U+10000..U+10FFFF\r\n\t\t\treturn result + 4;\r\n\t\t}\r\n\t};\r\n\r\n\tstruct utf8_writer\r\n\t{\r\n\t\ttypedef uint8_t* value_type;\r\n\r\n\t\tstatic value_type low(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\t// U+0000..U+007F\r\n\t\t\tif (ch < 0x80)\r\n\t\t\t{\r\n\t\t\t\t*result = static_cast<uint8_t>(ch);\r\n\t\t\t\treturn result + 1;\r\n\t\t\t}\r\n\t\t\t// U+0080..U+07FF\r\n\t\t\telse if (ch < 0x800)\r\n\t\t\t{\r\n\t\t\t\tresult[0] = static_cast<uint8_t>(0xC0 | (ch >> 6));\r\n\t\t\t\tresult[1] = static_cast<uint8_t>(0x80 | (ch & 0x3F));\r\n\t\t\t\treturn result + 2;\r\n\t\t\t}\r\n\t\t\t// U+0800..U+FFFF\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tresult[0] = static_cast<uint8_t>(0xE0 | (ch >> 12));\r\n\t\t\t\tresult[1] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));\r\n\t\t\t\tresult[2] = static_cast<uint8_t>(0x80 | (ch & 0x3F));\r\n\t\t\t\treturn result + 3;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tstatic value_type high(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\t// U+10000..U+10FFFF\r\n\t\t\tresult[0] = static_cast<uint8_t>(0xF0 | (ch >> 18));\r\n\t\t\tresult[1] = static_cast<uint8_t>(0x80 | ((ch >> 12) & 0x3F));\r\n\t\t\tresult[2] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));\r\n\t\t\tresult[3] = static_cast<uint8_t>(0x80 | (ch & 0x3F));\r\n\t\t\treturn result + 4;\r\n\t\t}\r\n\r\n\t\tstatic value_type any(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\treturn (ch < 0x10000) ? low(result, ch) : high(result, ch);\r\n\t\t}\r\n\t};\r\n\r\n\tstruct utf16_counter\r\n\t{\r\n\t\ttypedef size_t value_type;\r\n\r\n\t\tstatic value_type low(value_type result, uint32_t)\r\n\t\t{\r\n\t\t\treturn result + 1;\r\n\t\t}\r\n\r\n\t\tstatic value_type high(value_type result, uint32_t)\r\n\t\t{\r\n\t\t\treturn result + 2;\r\n\t\t}\r\n\t};\r\n\r\n\tstruct utf16_writer\r\n\t{\r\n\t\ttypedef uint16_t* value_type;\r\n\r\n\t\tstatic value_type low(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\t*result = static_cast<uint16_t>(ch);\r\n\r\n\t\t\treturn result + 1;\r\n\t\t}\r\n\r\n\t\tstatic value_type high(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\tuint32_t msh = static_cast<uint32_t>(ch - 0x10000) >> 10;\r\n\t\t\tuint32_t lsh = static_cast<uint32_t>(ch - 0x10000) & 0x3ff;\r\n\r\n\t\t\tresult[0] = static_cast<uint16_t>(0xD800 + msh);\r\n\t\t\tresult[1] = static_cast<uint16_t>(0xDC00 + lsh);\r\n\r\n\t\t\treturn result + 2;\r\n\t\t}\r\n\r\n\t\tstatic value_type any(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\treturn (ch < 0x10000) ? low(result, ch) : high(result, ch);\r\n\t\t}\r\n\t};\r\n\r\n\tstruct utf32_counter\r\n\t{\r\n\t\ttypedef size_t value_type;\r\n\r\n\t\tstatic value_type low(value_type result, uint32_t)\r\n\t\t{\r\n\t\t\treturn result + 1;\r\n\t\t}\r\n\r\n\t\tstatic value_type high(value_type result, uint32_t)\r\n\t\t{\r\n\t\t\treturn result + 1;\r\n\t\t}\r\n\t};\r\n\r\n\tstruct utf32_writer\r\n\t{\r\n\t\ttypedef uint32_t* value_type;\r\n\r\n\t\tstatic value_type low(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\t*result = ch;\r\n\r\n\t\t\treturn result + 1;\r\n\t\t}\r\n\r\n\t\tstatic value_type high(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\t*result = ch;\r\n\r\n\t\t\treturn result + 1;\r\n\t\t}\r\n\r\n\t\tstatic value_type any(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\t*result = ch;\r\n\r\n\t\t\treturn result + 1;\r\n\t\t}\r\n\t};\r\n\r\n\tstruct latin1_writer\r\n\t{\r\n\t\ttypedef uint8_t* value_type;\r\n\r\n\t\tstatic value_type low(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\t*result = static_cast<uint8_t>(ch > 255 ? '?' : ch);\r\n\r\n\t\t\treturn result + 1;\r\n\t\t}\r\n\r\n\t\tstatic value_type high(value_type result, uint32_t ch)\r\n\t\t{\r\n\t\t\t(void)ch;\r\n\r\n\t\t\t*result = '?';\r\n\r\n\t\t\treturn result + 1;\r\n\t\t}\r\n\t};\r\n\r\n\tstruct utf8_decoder\r\n\t{\r\n\t\ttypedef uint8_t type;\r\n\r\n\t\ttemplate <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits)\r\n\t\t{\r\n\t\t\tconst uint8_t utf8_byte_mask = 0x3f;\r\n\r\n\t\t\twhile (size)\r\n\t\t\t{\r\n\t\t\t\tuint8_t lead = *data;\r\n\r\n\t\t\t\t// 0xxxxxxx -> U+0000..U+007F\r\n\t\t\t\tif (lead < 0x80)\r\n\t\t\t\t{\r\n\t\t\t\t\tresult = Traits::low(result, lead);\r\n\t\t\t\t\tdata += 1;\r\n\t\t\t\t\tsize -= 1;\r\n\r\n\t\t\t\t\t// process aligned single-byte (ascii) blocks\r\n\t\t\t\t\tif ((reinterpret_cast<uintptr_t>(data) & 3) == 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// round-trip through void* to silence 'cast increases required alignment of target type' warnings\r\n\t\t\t\t\t\twhile (size >= 4 && (*static_cast<const uint32_t*>(static_cast<const void*>(data)) & 0x80808080) == 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tresult = Traits::low(result, data[0]);\r\n\t\t\t\t\t\t\tresult = Traits::low(result, data[1]);\r\n\t\t\t\t\t\t\tresult = Traits::low(result, data[2]);\r\n\t\t\t\t\t\t\tresult = Traits::low(result, data[3]);\r\n\t\t\t\t\t\t\tdata += 4;\r\n\t\t\t\t\t\t\tsize -= 4;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\t// 110xxxxx -> U+0080..U+07FF\r\n\t\t\t\telse if (static_cast<unsigned int>(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80)\r\n\t\t\t\t{\r\n\t\t\t\t\tresult = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask));\r\n\t\t\t\t\tdata += 2;\r\n\t\t\t\t\tsize -= 2;\r\n\t\t\t\t}\r\n\t\t\t\t// 1110xxxx -> U+0800-U+FFFF\r\n\t\t\t\telse if (static_cast<unsigned int>(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80)\r\n\t\t\t\t{\r\n\t\t\t\t\tresult = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask));\r\n\t\t\t\t\tdata += 3;\r\n\t\t\t\t\tsize -= 3;\r\n\t\t\t\t}\r\n\t\t\t\t// 11110xxx -> U+10000..U+10FFFF\r\n\t\t\t\telse if (static_cast<unsigned int>(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80)\r\n\t\t\t\t{\r\n\t\t\t\t\tresult = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask));\r\n\t\t\t\t\tdata += 4;\r\n\t\t\t\t\tsize -= 4;\r\n\t\t\t\t}\r\n\t\t\t\t// 10xxxxxx or 11111xxx -> invalid\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tdata += 1;\r\n\t\t\t\t\tsize -= 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\treturn result;\r\n\t\t}\r\n\t};\r\n\r\n\ttemplate <typename opt_swap> struct utf16_decoder\r\n\t{\r\n\t\ttypedef uint16_t type;\r\n\r\n\t\ttemplate <typename Traits> static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits)\r\n\t\t{\r\n\t\t\twhile (size)\r\n\t\t\t{\r\n\t\t\t\tuint16_t lead = opt_swap::value ? endian_swap(*data) : *data;\r\n\r\n\t\t\t\t// U+0000..U+D7FF\r\n\t\t\t\tif (lead < 0xD800)\r\n\t\t\t\t{\r\n\t\t\t\t\tresult = Traits::low(result, lead);\r\n\t\t\t\t\tdata += 1;\r\n\t\t\t\t\tsize -= 1;\r\n\t\t\t\t}\r\n\t\t\t\t// U+E000..U+FFFF\r\n\t\t\t\telse if (static_cast<unsigned int>(lead - 0xE000) < 0x2000)\r\n\t\t\t\t{\r\n\t\t\t\t\tresult = Traits::low(result, lead);\r\n\t\t\t\t\tdata += 1;\r\n\t\t\t\t\tsize -= 1;\r\n\t\t\t\t}\r\n\t\t\t\t// surrogate pair lead\r\n\t\t\t\telse if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && size >= 2)\r\n\t\t\t\t{\r\n\t\t\t\t\tuint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1];\r\n\r\n\t\t\t\t\tif (static_cast<unsigned int>(next - 0xDC00) < 0x400)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tresult = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff));\r\n\t\t\t\t\t\tdata += 2;\r\n\t\t\t\t\t\tsize -= 2;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tdata += 1;\r\n\t\t\t\t\t\tsize -= 1;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tdata += 1;\r\n\t\t\t\t\tsize -= 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\treturn result;\r\n\t\t}\r\n\t};\r\n\r\n\ttemplate <typename opt_swap> struct utf32_decoder\r\n\t{\r\n\t\ttypedef uint32_t type;\r\n\r\n\t\ttemplate <typename Traits> static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits)\r\n\t\t{\r\n\t\t\twhile (size)\r\n\t\t\t{\r\n\t\t\t\tuint32_t lead = opt_swap::value ? endian_swap(*data) : *data;\r\n\r\n\t\t\t\t// U+0000..U+FFFF\r\n\t\t\t\tif (lead < 0x10000)\r\n\t\t\t\t{\r\n\t\t\t\t\tresult = Traits::low(result, lead);\r\n\t\t\t\t\tdata += 1;\r\n\t\t\t\t\tsize -= 1;\r\n\t\t\t\t}\r\n\t\t\t\t// U+10000..U+10FFFF\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tresult = Traits::high(result, lead);\r\n\t\t\t\t\tdata += 1;\r\n\t\t\t\t\tsize -= 1;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\treturn result;\r\n\t\t}\r\n\t};\r\n\r\n\tstruct latin1_decoder\r\n\t{\r\n\t\ttypedef uint8_t type;\r\n\r\n\t\ttemplate <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits)\r\n\t\t{\r\n\t\t\twhile (size)\r\n\t\t\t{\r\n\t\t\t\tresult = Traits::low(result, *data);\r\n\t\t\t\tdata += 1;\r\n\t\t\t\tsize -= 1;\r\n\t\t\t}\r\n\r\n\t\t\treturn result;\r\n\t\t}\r\n\t};\r\n\r\n\ttemplate <size_t size> struct wchar_selector;\r\n\r\n\ttemplate <> struct wchar_selector<2>\r\n\t{\r\n\t\ttypedef uint16_t type;\r\n\t\ttypedef utf16_counter counter;\r\n\t\ttypedef utf16_writer writer;\r\n\t\ttypedef utf16_decoder<opt_false> decoder;\r\n\t};\r\n\r\n\ttemplate <> struct wchar_selector<4>\r\n\t{\r\n\t\ttypedef uint32_t type;\r\n\t\ttypedef utf32_counter counter;\r\n\t\ttypedef utf32_writer writer;\r\n\t\ttypedef utf32_decoder<opt_false> decoder;\r\n\t};\r\n\r\n\ttypedef wchar_selector<sizeof(wchar_t)>::counter wchar_counter;\r\n\ttypedef wchar_selector<sizeof(wchar_t)>::writer wchar_writer;\r\n\r\n\tstruct wchar_decoder\r\n\t{\r\n\t\ttypedef wchar_t type;\r\n\r\n\t\ttemplate <typename Traits> static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits)\r\n\t\t{\r\n\t\t\ttypedef wchar_selector<sizeof(wchar_t)>::decoder decoder;\r\n\r\n\t\t\treturn decoder::process(reinterpret_cast<const typename decoder::type*>(data), size, result, traits);\r\n\t\t}\r\n\t};\r\n\r\n#ifdef PUGIXML_WCHAR_MODE\r\n\tPUGI_IMPL_FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length)\r\n\t{\r\n\t\tfor (size_t i = 0; i < length; ++i)\r\n\t\t\tresult[i] = static_cast<wchar_t>(endian_swap(static_cast<wchar_selector<sizeof(wchar_t)>::type>(data[i])));\r\n\t}\r\n#endif\r\nPUGI_IMPL_NS_END\r\n\r\nPUGI_IMPL_NS_BEGIN\r\n\tenum chartype_t\r\n\t{\r\n\t\tct_parse_pcdata = 1,\t// \\0, &, \\r, <\r\n\t\tct_parse_attr = 2,\t\t// \\0, &, \\r, ', \"\r\n\t\tct_parse_attr_ws = 4,\t// \\0, &, \\r, ', \", \\n, tab\r\n\t\tct_space = 8,\t\t\t// \\r, \\n, space, tab\r\n\t\tct_parse_cdata = 16,\t// \\0, ], >, \\r\r\n\t\tct_parse_comment = 32,\t// \\0, -, >, \\r\r\n\t\tct_symbol = 64,\t\t\t// Any symbol > 127, a-z, A-Z, 0-9, _, :, -, .\r\n\t\tct_start_symbol = 128\t// Any symbol > 127, a-z, A-Z, _, :\r\n\t};\r\n\r\n\tstatic const unsigned char chartype_table[256] =\r\n\t{\r\n\t\t55,  0,   0,   0,   0,   0,   0,   0,      0,   12,  12,  0,   0,   63,  0,   0,   // 0-15\r\n\t\t0,   0,   0,   0,   0,   0,   0,   0,      0,   0,   0,   0,   0,   0,   0,   0,   // 16-31\r\n\t\t8,   0,   6,   0,   0,   0,   7,   6,      0,   0,   0,   0,   0,   96,  64,  0,   // 32-47\r\n\t\t64,  64,  64,  64,  64,  64,  64,  64,     64,  64,  192, 0,   1,   0,   48,  0,   // 48-63\r\n\t\t0,   192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, // 64-79\r\n\t\t192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 0,   0,   16,  0,   192, // 80-95\r\n\t\t0,   192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, // 96-111\r\n\t\t192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 0, 0, 0, 0, 0,           // 112-127\r\n\r\n\t\t192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, // 128+\r\n\t\t192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,\r\n\t\t192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,\r\n\t\t192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,\r\n\t\t192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,\r\n\t\t192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,\r\n\t\t192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,\r\n\t\t192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192\r\n\t};\r\n\r\n\tenum chartypex_t\r\n\t{\r\n\t\tctx_special_pcdata = 1,   // Any symbol >= 0 and < 32 (except \\t, \\r, \\n), &, <, >\r\n\t\tctx_special_attr = 2,     // Any symbol >= 0 and < 32, &, <, \", '\r\n\t\tctx_start_symbol = 4,\t  // Any symbol > 127, a-z, A-Z, _\r\n\t\tctx_digit = 8,\t\t\t  // 0-9\r\n\t\tctx_symbol = 16\t\t\t  // Any symbol > 127, a-z, A-Z, 0-9, _, -, .\r\n\t};\r\n\r\n\tstatic const unsigned char chartypex_table[256] =\r\n\t{\r\n\t\t3,  3,  3,  3,  3,  3,  3,  3,     3,  2,  2,  3,  3,  2,  3,  3,     // 0-15\r\n\t\t3,  3,  3,  3,  3,  3,  3,  3,     3,  3,  3,  3,  3,  3,  3,  3,     // 16-31\r\n\t\t0,  0,  2,  0,  0,  0,  3,  2,     0,  0,  0,  0,  0, 16, 16,  0,     // 32-47\r\n\t\t24, 24, 24, 24, 24, 24, 24, 24,    24, 24, 0,  0,  3,  0,  1,  0,     // 48-63\r\n\r\n\t\t0,  20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,    // 64-79\r\n\t\t20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 0,  0,  0,  0,  20,    // 80-95\r\n\t\t0,  20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,    // 96-111\r\n\t\t20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 0,  0,  0,  0,  0,     // 112-127\r\n\r\n\t\t20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,    // 128+\r\n\t\t20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,\r\n\t\t20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,\r\n\t\t20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,\r\n\t\t20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,\r\n\t\t20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,\r\n\t\t20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,\r\n\t\t20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20\r\n\t};\r\n\r\n#ifdef PUGIXML_WCHAR_MODE\r\n\t#define PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, table) ((static_cast<unsigned int>(c) < 128 ? table[static_cast<unsigned int>(c)] : table[128]) & (ct))\r\n#else\r\n\t#define PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast<unsigned char>(c)] & (ct))\r\n#endif\r\n\r\n\t#define PUGI_IMPL_IS_CHARTYPE(c, ct) PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, chartype_table)\r\n\t#define PUGI_IMPL_IS_CHARTYPEX(c, ct) PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, chartypex_table)\r\n\r\n\tPUGI_IMPL_FN bool is_little_endian()\r\n\t{\r\n\t\tunsigned int ui = 1;\r\n\r\n\t\treturn *reinterpret_cast<unsigned char*>(&ui) == 1;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_encoding get_wchar_encoding()\r\n\t{\r\n\t\tPUGI_IMPL_STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4);\r\n\r\n\t\tif (sizeof(wchar_t) == 2)\r\n\t\t\treturn is_little_endian() ? encoding_utf16_le : encoding_utf16_be;\r\n\t\telse\r\n\t\t\treturn is_little_endian() ? encoding_utf32_le : encoding_utf32_be;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length)\r\n\t{\r\n\t#define PUGI_IMPL_SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; }\r\n\t#define PUGI_IMPL_SCANCHARTYPE(ct) { while (offset < size && PUGI_IMPL_IS_CHARTYPE(data[offset], ct)) offset++; }\r\n\r\n\t\t// check if we have a non-empty XML declaration\r\n\t\tif (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI_IMPL_IS_CHARTYPE(data[5], ct_space)))\r\n\t\t\treturn false;\r\n\r\n\t\t// scan XML declaration until the encoding field\r\n\t\tfor (size_t i = 6; i + 1 < size; ++i)\r\n\t\t{\r\n\t\t\t// declaration can not contain ? in quoted values\r\n\t\t\tif (data[i] == '?')\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tif (data[i] == 'e' && data[i + 1] == 'n')\r\n\t\t\t{\r\n\t\t\t\tsize_t offset = i;\r\n\r\n\t\t\t\t// encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed\r\n\t\t\t\tPUGI_IMPL_SCANCHAR('e'); PUGI_IMPL_SCANCHAR('n'); PUGI_IMPL_SCANCHAR('c'); PUGI_IMPL_SCANCHAR('o');\r\n\t\t\t\tPUGI_IMPL_SCANCHAR('d'); PUGI_IMPL_SCANCHAR('i'); PUGI_IMPL_SCANCHAR('n'); PUGI_IMPL_SCANCHAR('g');\r\n\r\n\t\t\t\t// S? = S?\r\n\t\t\t\tPUGI_IMPL_SCANCHARTYPE(ct_space);\r\n\t\t\t\tPUGI_IMPL_SCANCHAR('=');\r\n\t\t\t\tPUGI_IMPL_SCANCHARTYPE(ct_space);\r\n\r\n\t\t\t\t// the only two valid delimiters are ' and \"\r\n\t\t\t\tuint8_t delimiter = (offset < size && data[offset] == '\"') ? '\"' : '\\'';\r\n\r\n\t\t\t\tPUGI_IMPL_SCANCHAR(delimiter);\r\n\r\n\t\t\t\tsize_t start = offset;\r\n\r\n\t\t\t\tout_encoding = data + offset;\r\n\r\n\t\t\t\tPUGI_IMPL_SCANCHARTYPE(ct_symbol);\r\n\r\n\t\t\t\tout_length = offset - start;\r\n\r\n\t\t\t\tPUGI_IMPL_SCANCHAR(delimiter);\r\n\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\r\n\t#undef PUGI_IMPL_SCANCHAR\r\n\t#undef PUGI_IMPL_SCANCHARTYPE\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size)\r\n\t{\r\n\t\t// skip encoding autodetection if input buffer is too small\r\n\t\tif (size < 4) return encoding_utf8;\r\n\r\n\t\tuint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3];\r\n\r\n\t\t// look for BOM in first few bytes\r\n\t\tif (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be;\r\n\t\tif (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le;\r\n\t\tif (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be;\r\n\t\tif (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le;\r\n\t\tif (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8;\r\n\r\n\t\t// look for <, <? or <?xm in various encodings\r\n\t\tif (d0 == 0 && d1 == 0 && d2 == 0 && d3 == 0x3c) return encoding_utf32_be;\r\n\t\tif (d0 == 0x3c && d1 == 0 && d2 == 0 && d3 == 0) return encoding_utf32_le;\r\n\t\tif (d0 == 0 && d1 == 0x3c && d2 == 0 && d3 == 0x3f) return encoding_utf16_be;\r\n\t\tif (d0 == 0x3c && d1 == 0 && d2 == 0x3f && d3 == 0) return encoding_utf16_le;\r\n\r\n\t\t// look for utf16 < followed by node name (this may fail, but is better than utf8 since it's zero terminated so early)\r\n\t\tif (d0 == 0 && d1 == 0x3c) return encoding_utf16_be;\r\n\t\tif (d0 == 0x3c && d1 == 0) return encoding_utf16_le;\r\n\r\n\t\t// no known BOM detected; parse declaration\r\n\t\tconst uint8_t* enc = 0;\r\n\t\tsize_t enc_length = 0;\r\n\r\n\t\tif (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d && parse_declaration_encoding(data, size, enc, enc_length))\r\n\t\t{\r\n\t\t\t// iso-8859-1 (case-insensitive)\r\n\t\t\tif (enc_length == 10\r\n\t\t\t\t&& (enc[0] | ' ') == 'i' && (enc[1] | ' ') == 's' && (enc[2] | ' ') == 'o'\r\n\t\t\t\t&& enc[3] == '-' && enc[4] == '8' && enc[5] == '8' && enc[6] == '5' && enc[7] == '9'\r\n\t\t\t\t&& enc[8] == '-' && enc[9] == '1')\r\n\t\t\t\treturn encoding_latin1;\r\n\r\n\t\t\t// latin1 (case-insensitive)\r\n\t\t\tif (enc_length == 6\r\n\t\t\t\t&& (enc[0] | ' ') == 'l' && (enc[1] | ' ') == 'a' && (enc[2] | ' ') == 't'\r\n\t\t\t\t&& (enc[3] | ' ') == 'i' && (enc[4] | ' ') == 'n'\r\n\t\t\t\t&& enc[5] == '1')\r\n\t\t\t\treturn encoding_latin1;\r\n\t\t}\r\n\r\n\t\treturn encoding_utf8;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void* contents, size_t size)\r\n\t{\r\n\t\t// replace wchar encoding with utf implementation\r\n\t\tif (encoding == encoding_wchar) return get_wchar_encoding();\r\n\r\n\t\t// replace utf16 encoding with utf16 with specific endianness\r\n\t\tif (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;\r\n\r\n\t\t// replace utf32 encoding with utf32 with specific endianness\r\n\t\tif (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;\r\n\r\n\t\t// only do autodetection if no explicit encoding is requested\r\n\t\tif (encoding != encoding_auto) return encoding;\r\n\r\n\t\t// try to guess encoding (based on XML specification, Appendix F.1)\r\n\t\tconst uint8_t* data = static_cast<const uint8_t*>(contents);\r\n\r\n\t\treturn guess_buffer_encoding(data, size);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)\r\n\t{\r\n\t\tsize_t length = size / sizeof(char_t);\r\n\r\n\t\tif (is_mutable)\r\n\t\t{\r\n\t\t\tout_buffer = static_cast<char_t*>(const_cast<void*>(contents));\r\n\t\t\tout_length = length;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tchar_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));\r\n\t\t\tif (!buffer) return false;\r\n\r\n\t\t\tif (contents)\r\n\t\t\t\tmemcpy(buffer, contents, length * sizeof(char_t));\r\n\t\t\telse\r\n\t\t\t\tassert(length == 0);\r\n\r\n\t\t\tbuffer[length] = 0;\r\n\r\n\t\t\tout_buffer = buffer;\r\n\t\t\tout_length = length + 1;\r\n\t\t}\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n#ifdef PUGIXML_WCHAR_MODE\r\n\tPUGI_IMPL_FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re)\r\n\t{\r\n\t\treturn (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) ||\r\n\t\t\t   (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)\r\n\t{\r\n\t\tconst char_t* data = static_cast<const char_t*>(contents);\r\n\t\tsize_t length = size / sizeof(char_t);\r\n\r\n\t\tif (is_mutable)\r\n\t\t{\r\n\t\t\tchar_t* buffer = const_cast<char_t*>(data);\r\n\r\n\t\t\tconvert_wchar_endian_swap(buffer, data, length);\r\n\r\n\t\t\tout_buffer = buffer;\r\n\t\t\tout_length = length;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tchar_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));\r\n\t\t\tif (!buffer) return false;\r\n\r\n\t\t\tconvert_wchar_endian_swap(buffer, data, length);\r\n\t\t\tbuffer[length] = 0;\r\n\r\n\t\t\tout_buffer = buffer;\r\n\t\t\tout_length = length + 1;\r\n\t\t}\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\ttemplate <typename D> PUGI_IMPL_FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)\r\n\t{\r\n\t\tconst typename D::type* data = static_cast<const typename D::type*>(contents);\r\n\t\tsize_t data_length = size / sizeof(typename D::type);\r\n\r\n\t\t// first pass: get length in wchar_t units\r\n\t\tsize_t length = D::process(data, data_length, 0, wchar_counter());\r\n\r\n\t\t// allocate buffer of suitable length\r\n\t\tchar_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));\r\n\t\tif (!buffer) return false;\r\n\r\n\t\t// second pass: convert utf16 input to wchar_t\r\n\t\twchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);\r\n\t\twchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer());\r\n\r\n\t\tassert(oend == obegin + length);\r\n\t\t*oend = 0;\r\n\r\n\t\tout_buffer = buffer;\r\n\t\tout_length = length + 1;\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)\r\n\t{\r\n\t\t// get native encoding\r\n\t\txml_encoding wchar_encoding = get_wchar_encoding();\r\n\r\n\t\t// fast path: no conversion required\r\n\t\tif (encoding == wchar_encoding)\r\n\t\t\treturn get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);\r\n\r\n\t\t// only endian-swapping is required\r\n\t\tif (need_endian_swap_utf(encoding, wchar_encoding))\r\n\t\t\treturn convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable);\r\n\r\n\t\t// source encoding is utf8\r\n\t\tif (encoding == encoding_utf8)\r\n\t\t\treturn convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder());\r\n\r\n\t\t// source encoding is utf16\r\n\t\tif (encoding == encoding_utf16_be || encoding == encoding_utf16_le)\r\n\t\t{\r\n\t\t\txml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;\r\n\r\n\t\t\treturn (native_encoding == encoding) ?\r\n\t\t\t\tconvert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) :\r\n\t\t\t\tconvert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>());\r\n\t\t}\r\n\r\n\t\t// source encoding is utf32\r\n\t\tif (encoding == encoding_utf32_be || encoding == encoding_utf32_le)\r\n\t\t{\r\n\t\t\txml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;\r\n\r\n\t\t\treturn (native_encoding == encoding) ?\r\n\t\t\t\tconvert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) :\r\n\t\t\t\tconvert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>());\r\n\t\t}\r\n\r\n\t\t// source encoding is latin1\r\n\t\tif (encoding == encoding_latin1)\r\n\t\t\treturn convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder());\r\n\r\n\t\tassert(false && \"Invalid encoding\"); // unreachable\r\n\t\treturn false;\r\n\t}\r\n#else\r\n\ttemplate <typename D> PUGI_IMPL_FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)\r\n\t{\r\n\t\tconst typename D::type* data = static_cast<const typename D::type*>(contents);\r\n\t\tsize_t data_length = size / sizeof(typename D::type);\r\n\r\n\t\t// first pass: get length in utf8 units\r\n\t\tsize_t length = D::process(data, data_length, 0, utf8_counter());\r\n\r\n\t\t// allocate buffer of suitable length\r\n\t\tchar_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));\r\n\t\tif (!buffer) return false;\r\n\r\n\t\t// second pass: convert utf16 input to utf8\r\n\t\tuint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);\r\n\t\tuint8_t* oend = D::process(data, data_length, obegin, utf8_writer());\r\n\r\n\t\tassert(oend == obegin + length);\r\n\t\t*oend = 0;\r\n\r\n\t\tout_buffer = buffer;\r\n\t\tout_length = length + 1;\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size)\r\n\t{\r\n\t\tfor (size_t i = 0; i < size; ++i)\r\n\t\t\tif (data[i] > 127)\r\n\t\t\t\treturn i;\r\n\r\n\t\treturn size;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)\r\n\t{\r\n\t\tconst uint8_t* data = static_cast<const uint8_t*>(contents);\r\n\t\tsize_t data_length = size;\r\n\r\n\t\t// get size of prefix that does not need utf8 conversion\r\n\t\tsize_t prefix_length = get_latin1_7bit_prefix_length(data, data_length);\r\n\t\tassert(prefix_length <= data_length);\r\n\r\n\t\tconst uint8_t* postfix = data + prefix_length;\r\n\t\tsize_t postfix_length = data_length - prefix_length;\r\n\r\n\t\t// if no conversion is needed, just return the original buffer\r\n\t\tif (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);\r\n\r\n\t\t// first pass: get length in utf8 units\r\n\t\tsize_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter());\r\n\r\n\t\t// allocate buffer of suitable length\r\n\t\tchar_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));\r\n\t\tif (!buffer) return false;\r\n\r\n\t\t// second pass: convert latin1 input to utf8\r\n\t\tmemcpy(buffer, data, prefix_length);\r\n\r\n\t\tuint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);\r\n\t\tuint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer());\r\n\r\n\t\tassert(oend == obegin + length);\r\n\t\t*oend = 0;\r\n\r\n\t\tout_buffer = buffer;\r\n\t\tout_length = length + 1;\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)\r\n\t{\r\n\t\t// fast path: no conversion required\r\n\t\tif (encoding == encoding_utf8)\r\n\t\t\treturn get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);\r\n\r\n\t\t// source encoding is utf16\r\n\t\tif (encoding == encoding_utf16_be || encoding == encoding_utf16_le)\r\n\t\t{\r\n\t\t\txml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;\r\n\r\n\t\t\treturn (native_encoding == encoding) ?\r\n\t\t\t\tconvert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) :\r\n\t\t\t\tconvert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>());\r\n\t\t}\r\n\r\n\t\t// source encoding is utf32\r\n\t\tif (encoding == encoding_utf32_be || encoding == encoding_utf32_le)\r\n\t\t{\r\n\t\t\txml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;\r\n\r\n\t\t\treturn (native_encoding == encoding) ?\r\n\t\t\t\tconvert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) :\r\n\t\t\t\tconvert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>());\r\n\t\t}\r\n\r\n\t\t// source encoding is latin1\r\n\t\tif (encoding == encoding_latin1)\r\n\t\t\treturn convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);\r\n\r\n\t\tassert(false && \"Invalid encoding\"); // unreachable\r\n\t\treturn false;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN size_t as_utf8_begin(const wchar_t* str, size_t length)\r\n\t{\r\n\t\t// get length in utf8 characters\r\n\t\treturn wchar_decoder::process(str, length, 0, utf8_counter());\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length)\r\n\t{\r\n\t\t// convert to utf8\r\n\t\tuint8_t* begin = reinterpret_cast<uint8_t*>(buffer);\r\n\t\tuint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer());\r\n\r\n\t\tassert(begin + size == end);\r\n\t\t(void)!end;\r\n\t\t(void)!size;\r\n\t}\r\n\r\n#ifndef PUGIXML_NO_STL\r\n\tPUGI_IMPL_FN std::string as_utf8_impl(const wchar_t* str, size_t length)\r\n\t{\r\n\t\t// first pass: get length in utf8 characters\r\n\t\tsize_t size = as_utf8_begin(str, length);\r\n\r\n\t\t// allocate resulting string\r\n\t\tstd::string result;\r\n\t\tresult.resize(size);\r\n\r\n\t\t// second pass: convert to utf8\r\n\t\tif (size > 0) as_utf8_end(&result[0], size, str, length);\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN std::basic_string<wchar_t> as_wide_impl(const char* str, size_t size)\r\n\t{\r\n\t\tconst uint8_t* data = reinterpret_cast<const uint8_t*>(str);\r\n\r\n\t\t// first pass: get length in wchar_t units\r\n\t\tsize_t length = utf8_decoder::process(data, size, 0, wchar_counter());\r\n\r\n\t\t// allocate resulting string\r\n\t\tstd::basic_string<wchar_t> result;\r\n\t\tresult.resize(length);\r\n\r\n\t\t// second pass: convert to wchar_t\r\n\t\tif (length > 0)\r\n\t\t{\r\n\t\t\twchar_writer::value_type begin = reinterpret_cast<wchar_writer::value_type>(&result[0]);\r\n\t\t\twchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer());\r\n\r\n\t\t\tassert(begin + length == end);\r\n\t\t\t(void)!end;\r\n\t\t}\r\n\r\n\t\treturn result;\r\n\t}\r\n#endif\r\n\r\n\ttemplate <typename Header>\r\n\tinline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target)\r\n\t{\r\n\t\t// never reuse shared memory\r\n\t\tif (header & xml_memory_page_contents_shared_mask) return false;\r\n\r\n\t\tsize_t target_length = strlength(target);\r\n\r\n\t\t// always reuse document buffer memory if possible\r\n\t\tif ((header & header_mask) == 0) return target_length >= length;\r\n\r\n\t\t// reuse heap memory if waste is not too great\r\n\t\tconst size_t reuse_threshold = 32;\r\n\r\n\t\treturn target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2);\r\n\t}\r\n\r\n\ttemplate <typename String, typename Header>\r\n\tPUGI_IMPL_FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length)\r\n\t{\r\n\t\tassert((header & header_mask) == 0 || dest); // header bit indicates whether dest was previously allocated\r\n\r\n\t\tif (source_length == 0)\r\n\t\t{\r\n\t\t\t// empty string and null pointer are equivalent, so just deallocate old memory\r\n\t\t\txml_allocator* alloc = PUGI_IMPL_GETPAGE_IMPL(header)->allocator;\r\n\r\n\t\t\tif (header & header_mask) alloc->deallocate_string(dest);\r\n\r\n\t\t\t// mark the string as not allocated\r\n\t\t\tdest = 0;\r\n\t\t\theader &= ~header_mask;\r\n\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\telse if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest))\r\n\t\t{\r\n\t\t\t// we can reuse old buffer, so just copy the new data (including zero terminator)\r\n\t\t\tmemcpy(dest, source, source_length * sizeof(char_t));\r\n\t\t\tdest[source_length] = 0;\r\n\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\txml_allocator* alloc = PUGI_IMPL_GETPAGE_IMPL(header)->allocator;\r\n\r\n\t\t\tif (!alloc->reserve()) return false;\r\n\r\n\t\t\t// allocate new buffer\r\n\t\t\tchar_t* buf = alloc->allocate_string(source_length + 1);\r\n\t\t\tif (!buf) return false;\r\n\r\n\t\t\t// copy the string (including zero terminator)\r\n\t\t\tmemcpy(buf, source, source_length * sizeof(char_t));\r\n\t\t\tbuf[source_length] = 0;\r\n\r\n\t\t\t// deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures)\r\n\t\t\tif (header & header_mask) alloc->deallocate_string(dest);\r\n\r\n\t\t\t// the string is now allocated, so set the flag\r\n\t\t\tdest = buf;\r\n\t\t\theader |= header_mask;\r\n\r\n\t\t\treturn true;\r\n\t\t}\r\n\t}\r\n\r\n\tstruct gap\r\n\t{\r\n\t\tchar_t* end;\r\n\t\tsize_t size;\r\n\r\n\t\tgap(): end(0), size(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\t// Push new gap, move s count bytes further (skipping the gap).\r\n\t\t// Collapse previous gap.\r\n\t\tvoid push(char_t*& s, size_t count)\r\n\t\t{\r\n\t\t\tif (end) // there was a gap already; collapse it\r\n\t\t\t{\r\n\t\t\t\t// Move [old_gap_end, new_gap_start) to [old_gap_start, ...)\r\n\t\t\t\tassert(s >= end);\r\n\t\t\t\tmemmove(end - size, end, reinterpret_cast<char*>(s) - reinterpret_cast<char*>(end));\r\n\t\t\t}\r\n\r\n\t\t\ts += count; // end of current gap\r\n\r\n\t\t\t// \"merge\" two gaps\r\n\t\t\tend = s;\r\n\t\t\tsize += count;\r\n\t\t}\r\n\r\n\t\t// Collapse all gaps, return past-the-end pointer\r\n\t\tchar_t* flush(char_t* s)\r\n\t\t{\r\n\t\t\tif (end)\r\n\t\t\t{\r\n\t\t\t\t// Move [old_gap_end, current_pos) to [old_gap_start, ...)\r\n\t\t\t\tassert(s >= end);\r\n\t\t\t\tmemmove(end - size, end, reinterpret_cast<char*>(s) - reinterpret_cast<char*>(end));\r\n\r\n\t\t\t\treturn s - size;\r\n\t\t\t}\r\n\t\t\telse return s;\r\n\t\t}\r\n\t};\r\n\r\n\tPUGI_IMPL_FN char_t* strconv_escape(char_t* s, gap& g)\r\n\t{\r\n\t\tchar_t* stre = s + 1;\r\n\r\n\t\tswitch (*stre)\r\n\t\t{\r\n\t\t\tcase '#':\t// &#...\r\n\t\t\t{\r\n\t\t\t\tunsigned int ucsc = 0;\r\n\r\n\t\t\t\tif (stre[1] == 'x') // &#x... (hex code)\r\n\t\t\t\t{\r\n\t\t\t\t\tstre += 2;\r\n\r\n\t\t\t\t\tchar_t ch = *stre;\r\n\r\n\t\t\t\t\tif (ch == ';') return stre;\r\n\r\n\t\t\t\t\tfor (;;)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (static_cast<unsigned int>(ch - '0') <= 9)\r\n\t\t\t\t\t\t\tucsc = 16 * ucsc + (ch - '0');\r\n\t\t\t\t\t\telse if (static_cast<unsigned int>((ch | ' ') - 'a') <= 5)\r\n\t\t\t\t\t\t\tucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10);\r\n\t\t\t\t\t\telse if (ch == ';')\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\telse // cancel\r\n\t\t\t\t\t\t\treturn stre;\r\n\r\n\t\t\t\t\t\tch = *++stre;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t++stre;\r\n\t\t\t\t}\r\n\t\t\t\telse\t// &#... (dec code)\r\n\t\t\t\t{\r\n\t\t\t\t\tchar_t ch = *++stre;\r\n\r\n\t\t\t\t\tif (ch == ';') return stre;\r\n\r\n\t\t\t\t\tfor (;;)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (static_cast<unsigned int>(ch - '0') <= 9)\r\n\t\t\t\t\t\t\tucsc = 10 * ucsc + (ch - '0');\r\n\t\t\t\t\t\telse if (ch == ';')\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\telse // cancel\r\n\t\t\t\t\t\t\treturn stre;\r\n\r\n\t\t\t\t\t\tch = *++stre;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t++stre;\r\n\t\t\t\t}\r\n\r\n\t\t\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\t\t\ts = reinterpret_cast<char_t*>(wchar_writer::any(reinterpret_cast<wchar_writer::value_type>(s), ucsc));\r\n\t\t\t#else\r\n\t\t\t\ts = reinterpret_cast<char_t*>(utf8_writer::any(reinterpret_cast<uint8_t*>(s), ucsc));\r\n\t\t\t#endif\r\n\r\n\t\t\t\tg.push(s, stre - s);\r\n\t\t\t\treturn stre;\r\n\t\t\t}\r\n\r\n\t\t\tcase 'a':\t// &a\r\n\t\t\t{\r\n\t\t\t\t++stre;\r\n\r\n\t\t\t\tif (*stre == 'm') // &am\r\n\t\t\t\t{\r\n\t\t\t\t\tif (*++stre == 'p' && *++stre == ';') // &amp;\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t*s++ = '&';\r\n\t\t\t\t\t\t++stre;\r\n\r\n\t\t\t\t\t\tg.push(s, stre - s);\r\n\t\t\t\t\t\treturn stre;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (*stre == 'p') // &ap\r\n\t\t\t\t{\r\n\t\t\t\t\tif (*++stre == 'o' && *++stre == 's' && *++stre == ';') // &apos;\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t*s++ = '\\'';\r\n\t\t\t\t\t\t++stre;\r\n\r\n\t\t\t\t\t\tg.push(s, stre - s);\r\n\t\t\t\t\t\treturn stre;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase 'g': // &g\r\n\t\t\t{\r\n\t\t\t\tif (*++stre == 't' && *++stre == ';') // &gt;\r\n\t\t\t\t{\r\n\t\t\t\t\t*s++ = '>';\r\n\t\t\t\t\t++stre;\r\n\r\n\t\t\t\t\tg.push(s, stre - s);\r\n\t\t\t\t\treturn stre;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase 'l': // &l\r\n\t\t\t{\r\n\t\t\t\tif (*++stre == 't' && *++stre == ';') // &lt;\r\n\t\t\t\t{\r\n\t\t\t\t\t*s++ = '<';\r\n\t\t\t\t\t++stre;\r\n\r\n\t\t\t\t\tg.push(s, stre - s);\r\n\t\t\t\t\treturn stre;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase 'q': // &q\r\n\t\t\t{\r\n\t\t\t\tif (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // &quot;\r\n\t\t\t\t{\r\n\t\t\t\t\t*s++ = '\"';\r\n\t\t\t\t\t++stre;\r\n\r\n\t\t\t\t\tg.push(s, stre - s);\r\n\t\t\t\t\treturn stre;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\treturn stre;\r\n\t}\r\n\r\n\t// Parser utilities\r\n\t#define PUGI_IMPL_ENDSWITH(c, e)        ((c) == (e) || ((c) == 0 && endch == (e)))\r\n\t#define PUGI_IMPL_SKIPWS()              { while (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) ++s; }\r\n\t#define PUGI_IMPL_OPTSET(OPT)           ( optmsk & (OPT) )\r\n\t#define PUGI_IMPL_PUSHNODE(TYPE)        { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI_IMPL_THROW_ERROR(status_out_of_memory, s); }\r\n\t#define PUGI_IMPL_POPNODE()             { cursor = cursor->parent; }\r\n\t#define PUGI_IMPL_SCANFOR(X)            { while (*s != 0 && !(X)) ++s; }\r\n\t#define PUGI_IMPL_SCANWHILE(X)          { while (X) ++s; }\r\n\t#define PUGI_IMPL_SCANWHILE_UNROLL(X)   { for (;;) { char_t ss = s[0]; if (PUGI_IMPL_UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 3; break; } s += 4; } }\r\n\t#define PUGI_IMPL_ENDSEG()              { ch = *s; *s = 0; ++s; }\r\n\t#define PUGI_IMPL_THROW_ERROR(err, m)   return error_offset = m, error_status = err, static_cast<char_t*>(0)\r\n\t#define PUGI_IMPL_CHECK_ERROR(err, m)   { if (*s == 0) PUGI_IMPL_THROW_ERROR(err, m); }\r\n\r\n\tPUGI_IMPL_FN char_t* strconv_comment(char_t* s, char_t endch)\r\n\t{\r\n\t\tgap g;\r\n\r\n\t\twhile (true)\r\n\t\t{\r\n\t\t\tPUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_comment));\r\n\r\n\t\t\tif (*s == '\\r') // Either a single 0x0d or 0x0d 0x0a pair\r\n\t\t\t{\r\n\t\t\t\t*s++ = '\\n'; // replace first one with 0x0a\r\n\r\n\t\t\t\tif (*s == '\\n') g.push(s, 1);\r\n\t\t\t}\r\n\t\t\telse if (s[0] == '-' && s[1] == '-' && PUGI_IMPL_ENDSWITH(s[2], '>')) // comment ends here\r\n\t\t\t{\r\n\t\t\t\t*g.flush(s) = 0;\r\n\r\n\t\t\t\treturn s + (s[2] == '>' ? 3 : 2);\r\n\t\t\t}\r\n\t\t\telse if (*s == 0)\r\n\t\t\t{\r\n\t\t\t\treturn 0;\r\n\t\t\t}\r\n\t\t\telse ++s;\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN char_t* strconv_cdata(char_t* s, char_t endch)\r\n\t{\r\n\t\tgap g;\r\n\r\n\t\twhile (true)\r\n\t\t{\r\n\t\t\tPUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_cdata));\r\n\r\n\t\t\tif (*s == '\\r') // Either a single 0x0d or 0x0d 0x0a pair\r\n\t\t\t{\r\n\t\t\t\t*s++ = '\\n'; // replace first one with 0x0a\r\n\r\n\t\t\t\tif (*s == '\\n') g.push(s, 1);\r\n\t\t\t}\r\n\t\t\telse if (s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>')) // CDATA ends here\r\n\t\t\t{\r\n\t\t\t\t*g.flush(s) = 0;\r\n\r\n\t\t\t\treturn s + 1;\r\n\t\t\t}\r\n\t\t\telse if (*s == 0)\r\n\t\t\t{\r\n\t\t\t\treturn 0;\r\n\t\t\t}\r\n\t\t\telse ++s;\r\n\t\t}\r\n\t}\r\n\r\n\ttypedef char_t* (*strconv_pcdata_t)(char_t*);\r\n\r\n\ttemplate <typename opt_trim, typename opt_eol, typename opt_escape> struct strconv_pcdata_impl\r\n\t{\r\n\t\tstatic char_t* parse(char_t* s)\r\n\t\t{\r\n\t\t\tgap g;\r\n\r\n\t\t\tchar_t* begin = s;\r\n\r\n\t\t\twhile (true)\r\n\t\t\t{\r\n\t\t\t\tPUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_pcdata));\r\n\r\n\t\t\t\tif (*s == '<') // PCDATA ends here\r\n\t\t\t\t{\r\n\t\t\t\t\tchar_t* end = g.flush(s);\r\n\r\n\t\t\t\t\tif (opt_trim::value)\r\n\t\t\t\t\t\twhile (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space))\r\n\t\t\t\t\t\t\t--end;\r\n\r\n\t\t\t\t\t*end = 0;\r\n\r\n\t\t\t\t\treturn s + 1;\r\n\t\t\t\t}\r\n\t\t\t\telse if (opt_eol::value && *s == '\\r') // Either a single 0x0d or 0x0d 0x0a pair\r\n\t\t\t\t{\r\n\t\t\t\t\t*s++ = '\\n'; // replace first one with 0x0a\r\n\r\n\t\t\t\t\tif (*s == '\\n') g.push(s, 1);\r\n\t\t\t\t}\r\n\t\t\t\telse if (opt_escape::value && *s == '&')\r\n\t\t\t\t{\r\n\t\t\t\t\ts = strconv_escape(s, g);\r\n\t\t\t\t}\r\n\t\t\t\telse if (*s == 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tchar_t* end = g.flush(s);\r\n\r\n\t\t\t\t\tif (opt_trim::value)\r\n\t\t\t\t\t\twhile (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space))\r\n\t\t\t\t\t\t\t--end;\r\n\r\n\t\t\t\t\t*end = 0;\r\n\r\n\t\t\t\t\treturn s;\r\n\t\t\t\t}\r\n\t\t\t\telse ++s;\r\n\t\t\t}\r\n\t\t}\r\n\t};\r\n\r\n\tPUGI_IMPL_FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask)\r\n\t{\r\n\t\tPUGI_IMPL_STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800);\r\n\r\n\t\tswitch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (trim eol escapes); this simultaneously checks 3 options from assertion above\r\n\t\t{\r\n\t\tcase 0: return strconv_pcdata_impl<opt_false, opt_false, opt_false>::parse;\r\n\t\tcase 1: return strconv_pcdata_impl<opt_false, opt_false, opt_true>::parse;\r\n\t\tcase 2: return strconv_pcdata_impl<opt_false, opt_true, opt_false>::parse;\r\n\t\tcase 3: return strconv_pcdata_impl<opt_false, opt_true, opt_true>::parse;\r\n\t\tcase 4: return strconv_pcdata_impl<opt_true, opt_false, opt_false>::parse;\r\n\t\tcase 5: return strconv_pcdata_impl<opt_true, opt_false, opt_true>::parse;\r\n\t\tcase 6: return strconv_pcdata_impl<opt_true, opt_true, opt_false>::parse;\r\n\t\tcase 7: return strconv_pcdata_impl<opt_true, opt_true, opt_true>::parse;\r\n\t\tdefault: assert(false); return 0; // unreachable\r\n\t\t}\r\n\t}\r\n\r\n\ttypedef char_t* (*strconv_attribute_t)(char_t*, char_t);\r\n\r\n\ttemplate <typename opt_escape> struct strconv_attribute_impl\r\n\t{\r\n\t\tstatic char_t* parse_wnorm(char_t* s, char_t end_quote)\r\n\t\t{\r\n\t\t\tgap g;\r\n\r\n\t\t\t// trim leading whitespaces\r\n\t\t\tif (PUGI_IMPL_IS_CHARTYPE(*s, ct_space))\r\n\t\t\t{\r\n\t\t\t\tchar_t* str = s;\r\n\r\n\t\t\t\tdo ++str;\r\n\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPE(*str, ct_space));\r\n\r\n\t\t\t\tg.push(s, str - s);\r\n\t\t\t}\r\n\r\n\t\t\twhile (true)\r\n\t\t\t{\r\n\t\t\t\tPUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space));\r\n\r\n\t\t\t\tif (*s == end_quote)\r\n\t\t\t\t{\r\n\t\t\t\t\tchar_t* str = g.flush(s);\r\n\r\n\t\t\t\t\tdo *str-- = 0;\r\n\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPE(*str, ct_space));\r\n\r\n\t\t\t\t\treturn s + 1;\r\n\t\t\t\t}\r\n\t\t\t\telse if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space))\r\n\t\t\t\t{\r\n\t\t\t\t\t*s++ = ' ';\r\n\r\n\t\t\t\t\tif (PUGI_IMPL_IS_CHARTYPE(*s, ct_space))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tchar_t* str = s + 1;\r\n\t\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPE(*str, ct_space)) ++str;\r\n\r\n\t\t\t\t\t\tg.push(s, str - s);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (opt_escape::value && *s == '&')\r\n\t\t\t\t{\r\n\t\t\t\t\ts = strconv_escape(s, g);\r\n\t\t\t\t}\r\n\t\t\t\telse if (!*s)\r\n\t\t\t\t{\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\t}\r\n\t\t\t\telse ++s;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tstatic char_t* parse_wconv(char_t* s, char_t end_quote)\r\n\t\t{\r\n\t\t\tgap g;\r\n\r\n\t\t\twhile (true)\r\n\t\t\t{\r\n\t\t\t\tPUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr_ws));\r\n\r\n\t\t\t\tif (*s == end_quote)\r\n\t\t\t\t{\r\n\t\t\t\t\t*g.flush(s) = 0;\r\n\r\n\t\t\t\t\treturn s + 1;\r\n\t\t\t\t}\r\n\t\t\t\telse if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space))\r\n\t\t\t\t{\r\n\t\t\t\t\tif (*s == '\\r')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t*s++ = ' ';\r\n\r\n\t\t\t\t\t\tif (*s == '\\n') g.push(s, 1);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse *s++ = ' ';\r\n\t\t\t\t}\r\n\t\t\t\telse if (opt_escape::value && *s == '&')\r\n\t\t\t\t{\r\n\t\t\t\t\ts = strconv_escape(s, g);\r\n\t\t\t\t}\r\n\t\t\t\telse if (!*s)\r\n\t\t\t\t{\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\t}\r\n\t\t\t\telse ++s;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tstatic char_t* parse_eol(char_t* s, char_t end_quote)\r\n\t\t{\r\n\t\t\tgap g;\r\n\r\n\t\t\twhile (true)\r\n\t\t\t{\r\n\t\t\t\tPUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr));\r\n\r\n\t\t\t\tif (*s == end_quote)\r\n\t\t\t\t{\r\n\t\t\t\t\t*g.flush(s) = 0;\r\n\r\n\t\t\t\t\treturn s + 1;\r\n\t\t\t\t}\r\n\t\t\t\telse if (*s == '\\r')\r\n\t\t\t\t{\r\n\t\t\t\t\t*s++ = '\\n';\r\n\r\n\t\t\t\t\tif (*s == '\\n') g.push(s, 1);\r\n\t\t\t\t}\r\n\t\t\t\telse if (opt_escape::value && *s == '&')\r\n\t\t\t\t{\r\n\t\t\t\t\ts = strconv_escape(s, g);\r\n\t\t\t\t}\r\n\t\t\t\telse if (!*s)\r\n\t\t\t\t{\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\t}\r\n\t\t\t\telse ++s;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tstatic char_t* parse_simple(char_t* s, char_t end_quote)\r\n\t\t{\r\n\t\t\tgap g;\r\n\r\n\t\t\twhile (true)\r\n\t\t\t{\r\n\t\t\t\tPUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr));\r\n\r\n\t\t\t\tif (*s == end_quote)\r\n\t\t\t\t{\r\n\t\t\t\t\t*g.flush(s) = 0;\r\n\r\n\t\t\t\t\treturn s + 1;\r\n\t\t\t\t}\r\n\t\t\t\telse if (opt_escape::value && *s == '&')\r\n\t\t\t\t{\r\n\t\t\t\t\ts = strconv_escape(s, g);\r\n\t\t\t\t}\r\n\t\t\t\telse if (!*s)\r\n\t\t\t\t{\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\t}\r\n\t\t\t\telse ++s;\r\n\t\t\t}\r\n\t\t}\r\n\t};\r\n\r\n\tPUGI_IMPL_FN strconv_attribute_t get_strconv_attribute(unsigned int optmask)\r\n\t{\r\n\t\tPUGI_IMPL_STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80);\r\n\r\n\t\tswitch ((optmask >> 4) & 15) // get bitmask for flags (wnorm wconv eol escapes); this simultaneously checks 4 options from assertion above\r\n\t\t{\r\n\t\tcase 0:  return strconv_attribute_impl<opt_false>::parse_simple;\r\n\t\tcase 1:  return strconv_attribute_impl<opt_true>::parse_simple;\r\n\t\tcase 2:  return strconv_attribute_impl<opt_false>::parse_eol;\r\n\t\tcase 3:  return strconv_attribute_impl<opt_true>::parse_eol;\r\n\t\tcase 4:  return strconv_attribute_impl<opt_false>::parse_wconv;\r\n\t\tcase 5:  return strconv_attribute_impl<opt_true>::parse_wconv;\r\n\t\tcase 6:  return strconv_attribute_impl<opt_false>::parse_wconv;\r\n\t\tcase 7:  return strconv_attribute_impl<opt_true>::parse_wconv;\r\n\t\tcase 8:  return strconv_attribute_impl<opt_false>::parse_wnorm;\r\n\t\tcase 9:  return strconv_attribute_impl<opt_true>::parse_wnorm;\r\n\t\tcase 10: return strconv_attribute_impl<opt_false>::parse_wnorm;\r\n\t\tcase 11: return strconv_attribute_impl<opt_true>::parse_wnorm;\r\n\t\tcase 12: return strconv_attribute_impl<opt_false>::parse_wnorm;\r\n\t\tcase 13: return strconv_attribute_impl<opt_true>::parse_wnorm;\r\n\t\tcase 14: return strconv_attribute_impl<opt_false>::parse_wnorm;\r\n\t\tcase 15: return strconv_attribute_impl<opt_true>::parse_wnorm;\r\n\t\tdefault: assert(false); return 0; // unreachable\r\n\t\t}\r\n\t}\r\n\r\n\tinline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0)\r\n\t{\r\n\t\txml_parse_result result;\r\n\t\tresult.status = status;\r\n\t\tresult.offset = offset;\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tstruct xml_parser\r\n\t{\r\n\t\txml_allocator* alloc;\r\n\t\tchar_t* error_offset;\r\n\t\txml_parse_status error_status;\r\n\r\n\t\txml_parser(xml_allocator* alloc_): alloc(alloc_), error_offset(0), error_status(status_ok)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\t// DOCTYPE consists of nested sections of the following possible types:\r\n\t\t// <!-- ... -->, <? ... ?>, \"...\", '...'\r\n\t\t// <![...]]>\r\n\t\t// <!...>\r\n\t\t// First group can not contain nested groups\r\n\t\t// Second group can contain nested groups of the same type\r\n\t\t// Third group can contain all other groups\r\n\t\tchar_t* parse_doctype_primitive(char_t* s)\r\n\t\t{\r\n\t\t\tif (*s == '\"' || *s == '\\'')\r\n\t\t\t{\r\n\t\t\t\t// quoted string\r\n\t\t\t\tchar_t ch = *s++;\r\n\t\t\t\tPUGI_IMPL_SCANFOR(*s == ch);\r\n\t\t\t\tif (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);\r\n\r\n\t\t\t\ts++;\r\n\t\t\t}\r\n\t\t\telse if (s[0] == '<' && s[1] == '?')\r\n\t\t\t{\r\n\t\t\t\t// <? ... ?>\r\n\t\t\t\ts += 2;\r\n\t\t\t\tPUGI_IMPL_SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype\r\n\t\t\t\tif (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);\r\n\r\n\t\t\t\ts += 2;\r\n\t\t\t}\r\n\t\t\telse if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-')\r\n\t\t\t{\r\n\t\t\t\ts += 4;\r\n\t\t\t\tPUGI_IMPL_SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype\r\n\t\t\t\tif (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);\r\n\r\n\t\t\t\ts += 3;\r\n\t\t\t}\r\n\t\t\telse PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);\r\n\r\n\t\t\treturn s;\r\n\t\t}\r\n\r\n\t\tchar_t* parse_doctype_ignore(char_t* s)\r\n\t\t{\r\n\t\t\tsize_t depth = 0;\r\n\r\n\t\t\tassert(s[0] == '<' && s[1] == '!' && s[2] == '[');\r\n\t\t\ts += 3;\r\n\r\n\t\t\twhile (*s)\r\n\t\t\t{\r\n\t\t\t\tif (s[0] == '<' && s[1] == '!' && s[2] == '[')\r\n\t\t\t\t{\r\n\t\t\t\t\t// nested ignore section\r\n\t\t\t\t\ts += 3;\r\n\t\t\t\t\tdepth++;\r\n\t\t\t\t}\r\n\t\t\t\telse if (s[0] == ']' && s[1] == ']' && s[2] == '>')\r\n\t\t\t\t{\r\n\t\t\t\t\t// ignore section end\r\n\t\t\t\t\ts += 3;\r\n\r\n\t\t\t\t\tif (depth == 0)\r\n\t\t\t\t\t\treturn s;\r\n\r\n\t\t\t\t\tdepth--;\r\n\t\t\t\t}\r\n\t\t\t\telse s++;\r\n\t\t\t}\r\n\r\n\t\t\tPUGI_IMPL_THROW_ERROR(status_bad_doctype, s);\r\n\t\t}\r\n\r\n\t\tchar_t* parse_doctype_group(char_t* s, char_t endch)\r\n\t\t{\r\n\t\t\tsize_t depth = 0;\r\n\r\n\t\t\tassert((s[0] == '<' || s[0] == 0) && s[1] == '!');\r\n\t\t\ts += 2;\r\n\r\n\t\t\twhile (*s)\r\n\t\t\t{\r\n\t\t\t\tif (s[0] == '<' && s[1] == '!' && s[2] != '-')\r\n\t\t\t\t{\r\n\t\t\t\t\tif (s[2] == '[')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// ignore\r\n\t\t\t\t\t\ts = parse_doctype_ignore(s);\r\n\t\t\t\t\t\tif (!s) return s;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// some control group\r\n\t\t\t\t\t\ts += 2;\r\n\t\t\t\t\t\tdepth++;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (s[0] == '<' || s[0] == '\"' || s[0] == '\\'')\r\n\t\t\t\t{\r\n\t\t\t\t\t// unknown tag (forbidden), or some primitive group\r\n\t\t\t\t\ts = parse_doctype_primitive(s);\r\n\t\t\t\t\tif (!s) return s;\r\n\t\t\t\t}\r\n\t\t\t\telse if (*s == '>')\r\n\t\t\t\t{\r\n\t\t\t\t\tif (depth == 0)\r\n\t\t\t\t\t\treturn s;\r\n\r\n\t\t\t\t\tdepth--;\r\n\t\t\t\t\ts++;\r\n\t\t\t\t}\r\n\t\t\t\telse s++;\r\n\t\t\t}\r\n\r\n\t\t\tif (depth != 0 || endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);\r\n\r\n\t\t\treturn s;\r\n\t\t}\r\n\r\n\t\tchar_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch)\r\n\t\t{\r\n\t\t\t// parse node contents, starting with exclamation mark\r\n\t\t\t++s;\r\n\r\n\t\t\tif (*s == '-') // '<!-...'\r\n\t\t\t{\r\n\t\t\t\t++s;\r\n\r\n\t\t\t\tif (*s == '-') // '<!--...'\r\n\t\t\t\t{\r\n\t\t\t\t\t++s;\r\n\r\n\t\t\t\t\tif (PUGI_IMPL_OPTSET(parse_comments))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tPUGI_IMPL_PUSHNODE(node_comment); // Append a new node on the tree.\r\n\t\t\t\t\t\tcursor->value = s; // Save the offset.\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (PUGI_IMPL_OPTSET(parse_eol) && PUGI_IMPL_OPTSET(parse_comments))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ts = strconv_comment(s, endch);\r\n\r\n\t\t\t\t\t\tif (!s) PUGI_IMPL_THROW_ERROR(status_bad_comment, cursor->value);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// Scan for terminating '-->'.\r\n\t\t\t\t\t\tPUGI_IMPL_SCANFOR(s[0] == '-' && s[1] == '-' && PUGI_IMPL_ENDSWITH(s[2], '>'));\r\n\t\t\t\t\t\tPUGI_IMPL_CHECK_ERROR(status_bad_comment, s);\r\n\r\n\t\t\t\t\t\tif (PUGI_IMPL_OPTSET(parse_comments))\r\n\t\t\t\t\t\t\t*s = 0; // Zero-terminate this segment at the first terminating '-'.\r\n\r\n\t\t\t\t\t\ts += (s[2] == '>' ? 3 : 2); // Step over the '\\0->'.\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse PUGI_IMPL_THROW_ERROR(status_bad_comment, s);\r\n\t\t\t}\r\n\t\t\telse if (*s == '[')\r\n\t\t\t{\r\n\t\t\t\t// '<![CDATA[...'\r\n\t\t\t\tif (*++s=='C' && *++s=='D' && *++s=='A' && *++s=='T' && *++s=='A' && *++s == '[')\r\n\t\t\t\t{\r\n\t\t\t\t\t++s;\r\n\r\n\t\t\t\t\tif (PUGI_IMPL_OPTSET(parse_cdata))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tPUGI_IMPL_PUSHNODE(node_cdata); // Append a new node on the tree.\r\n\t\t\t\t\t\tcursor->value = s; // Save the offset.\r\n\r\n\t\t\t\t\t\tif (PUGI_IMPL_OPTSET(parse_eol))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\ts = strconv_cdata(s, endch);\r\n\r\n\t\t\t\t\t\t\tif (!s) PUGI_IMPL_THROW_ERROR(status_bad_cdata, cursor->value);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t// Scan for terminating ']]>'.\r\n\t\t\t\t\t\t\tPUGI_IMPL_SCANFOR(s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>'));\r\n\t\t\t\t\t\t\tPUGI_IMPL_CHECK_ERROR(status_bad_cdata, s);\r\n\r\n\t\t\t\t\t\t\t*s++ = 0; // Zero-terminate this segment.\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse // Flagged for discard, but we still have to scan for the terminator.\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// Scan for terminating ']]>'.\r\n\t\t\t\t\t\tPUGI_IMPL_SCANFOR(s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>'));\r\n\t\t\t\t\t\tPUGI_IMPL_CHECK_ERROR(status_bad_cdata, s);\r\n\r\n\t\t\t\t\t\t++s;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\ts += (s[1] == '>' ? 2 : 1); // Step over the last ']>'.\r\n\t\t\t\t}\r\n\t\t\t\telse PUGI_IMPL_THROW_ERROR(status_bad_cdata, s);\r\n\t\t\t}\r\n\t\t\telse if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI_IMPL_ENDSWITH(s[6], 'E'))\r\n\t\t\t{\r\n\t\t\t\ts -= 2;\r\n\r\n\t\t\t\tif (cursor->parent) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s);\r\n\r\n\t\t\t\tchar_t* mark = s + 9;\r\n\r\n\t\t\t\ts = parse_doctype_group(s, endch);\r\n\t\t\t\tif (!s) return s;\r\n\r\n\t\t\t\tassert((*s == 0 && endch == '>') || *s == '>');\r\n\t\t\t\tif (*s) *s++ = 0;\r\n\r\n\t\t\t\tif (PUGI_IMPL_OPTSET(parse_doctype))\r\n\t\t\t\t{\r\n\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPE(*mark, ct_space)) ++mark;\r\n\r\n\t\t\t\t\tPUGI_IMPL_PUSHNODE(node_doctype);\r\n\r\n\t\t\t\t\tcursor->value = mark;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (*s == 0 && endch == '-') PUGI_IMPL_THROW_ERROR(status_bad_comment, s);\r\n\t\t\telse if (*s == 0 && endch == '[') PUGI_IMPL_THROW_ERROR(status_bad_cdata, s);\r\n\t\t\telse PUGI_IMPL_THROW_ERROR(status_unrecognized_tag, s);\r\n\r\n\t\t\treturn s;\r\n\t\t}\r\n\r\n\t\tchar_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch)\r\n\t\t{\r\n\t\t\t// load into registers\r\n\t\t\txml_node_struct* cursor = ref_cursor;\r\n\t\t\tchar_t ch = 0;\r\n\r\n\t\t\t// parse node contents, starting with question mark\r\n\t\t\t++s;\r\n\r\n\t\t\t// read PI target\r\n\t\t\tchar_t* target = s;\r\n\r\n\t\t\tif (!PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) PUGI_IMPL_THROW_ERROR(status_bad_pi, s);\r\n\r\n\t\t\tPUGI_IMPL_SCANWHILE(PUGI_IMPL_IS_CHARTYPE(*s, ct_symbol));\r\n\t\t\tPUGI_IMPL_CHECK_ERROR(status_bad_pi, s);\r\n\r\n\t\t\t// determine node type; stricmp / strcasecmp is not portable\r\n\t\t\tbool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s;\r\n\r\n\t\t\tif (declaration ? PUGI_IMPL_OPTSET(parse_declaration) : PUGI_IMPL_OPTSET(parse_pi))\r\n\t\t\t{\r\n\t\t\t\tif (declaration)\r\n\t\t\t\t{\r\n\t\t\t\t\t// disallow non top-level declarations\r\n\t\t\t\t\tif (cursor->parent) PUGI_IMPL_THROW_ERROR(status_bad_pi, s);\r\n\r\n\t\t\t\t\tPUGI_IMPL_PUSHNODE(node_declaration);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tPUGI_IMPL_PUSHNODE(node_pi);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcursor->name = target;\r\n\r\n\t\t\t\tPUGI_IMPL_ENDSEG();\r\n\r\n\t\t\t\t// parse value/attributes\r\n\t\t\t\tif (ch == '?')\r\n\t\t\t\t{\r\n\t\t\t\t\t// empty node\r\n\t\t\t\t\tif (!PUGI_IMPL_ENDSWITH(*s, '>')) PUGI_IMPL_THROW_ERROR(status_bad_pi, s);\r\n\t\t\t\t\ts += (*s == '>');\r\n\r\n\t\t\t\t\tPUGI_IMPL_POPNODE();\r\n\t\t\t\t}\r\n\t\t\t\telse if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space))\r\n\t\t\t\t{\r\n\t\t\t\t\tPUGI_IMPL_SKIPWS();\r\n\r\n\t\t\t\t\t// scan for tag end\r\n\t\t\t\t\tchar_t* value = s;\r\n\r\n\t\t\t\t\tPUGI_IMPL_SCANFOR(s[0] == '?' && PUGI_IMPL_ENDSWITH(s[1], '>'));\r\n\t\t\t\t\tPUGI_IMPL_CHECK_ERROR(status_bad_pi, s);\r\n\r\n\t\t\t\t\tif (declaration)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// replace ending ? with / so that 'element' terminates properly\r\n\t\t\t\t\t\t*s = '/';\r\n\r\n\t\t\t\t\t\t// we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES\r\n\t\t\t\t\t\ts = value;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// store value and step over >\r\n\t\t\t\t\t\tcursor->value = value;\r\n\r\n\t\t\t\t\t\tPUGI_IMPL_POPNODE();\r\n\r\n\t\t\t\t\t\tPUGI_IMPL_ENDSEG();\r\n\r\n\t\t\t\t\t\ts += (*s == '>');\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse PUGI_IMPL_THROW_ERROR(status_bad_pi, s);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// scan for tag end\r\n\t\t\t\tPUGI_IMPL_SCANFOR(s[0] == '?' && PUGI_IMPL_ENDSWITH(s[1], '>'));\r\n\t\t\t\tPUGI_IMPL_CHECK_ERROR(status_bad_pi, s);\r\n\r\n\t\t\t\ts += (s[1] == '>' ? 2 : 1);\r\n\t\t\t}\r\n\r\n\t\t\t// store from registers\r\n\t\t\tref_cursor = cursor;\r\n\r\n\t\t\treturn s;\r\n\t\t}\r\n\r\n\t\tchar_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch)\r\n\t\t{\r\n\t\t\tstrconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk);\r\n\t\t\tstrconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk);\r\n\r\n\t\t\tchar_t ch = 0;\r\n\t\t\txml_node_struct* cursor = root;\r\n\t\t\tchar_t* mark = s;\r\n\t\t\tchar_t* merged_pcdata = s;\r\n\r\n\t\t\twhile (*s != 0)\r\n\t\t\t{\r\n\t\t\t\tif (*s == '<')\r\n\t\t\t\t{\r\n\t\t\t\t\t++s;\r\n\r\n\t\t\t\tLOC_TAG:\r\n\t\t\t\t\tif (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) // '<#...'\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tPUGI_IMPL_PUSHNODE(node_element); // Append a new node to the tree.\r\n\r\n\t\t\t\t\t\tcursor->name = s;\r\n\r\n\t\t\t\t\t\tPUGI_IMPL_SCANWHILE_UNROLL(PUGI_IMPL_IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.\r\n\t\t\t\t\t\tPUGI_IMPL_ENDSEG(); // Save char in 'ch', terminate & step over.\r\n\r\n\t\t\t\t\t\tif (ch == '>')\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t// end of tag\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\tLOC_ATTRIBUTES:\r\n\t\t\t\t\t\t\twhile (true)\r\n\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\tPUGI_IMPL_SKIPWS(); // Eat any whitespace.\r\n\r\n\t\t\t\t\t\t\t\tif (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) // <... #...\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\txml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute.\r\n\t\t\t\t\t\t\t\t\tif (!a) PUGI_IMPL_THROW_ERROR(status_out_of_memory, s);\r\n\r\n\t\t\t\t\t\t\t\t\ta->name = s; // Save the offset.\r\n\r\n\t\t\t\t\t\t\t\t\tPUGI_IMPL_SCANWHILE_UNROLL(PUGI_IMPL_IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.\r\n\t\t\t\t\t\t\t\t\tPUGI_IMPL_ENDSEG(); // Save char in 'ch', terminate & step over.\r\n\r\n\t\t\t\t\t\t\t\t\tif (PUGI_IMPL_IS_CHARTYPE(ch, ct_space))\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tPUGI_IMPL_SKIPWS(); // Eat any whitespace.\r\n\r\n\t\t\t\t\t\t\t\t\t\tch = *s;\r\n\t\t\t\t\t\t\t\t\t\t++s;\r\n\t\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\t\tif (ch == '=') // '<... #=...'\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tPUGI_IMPL_SKIPWS(); // Eat any whitespace.\r\n\r\n\t\t\t\t\t\t\t\t\t\tif (*s == '\"' || *s == '\\'') // '<... #=\"...'\r\n\t\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\t\tch = *s; // Save quote char to avoid breaking on \"''\" -or- '\"\"'.\r\n\t\t\t\t\t\t\t\t\t\t\t++s; // Step over the quote.\r\n\t\t\t\t\t\t\t\t\t\t\ta->value = s; // Save the offset.\r\n\r\n\t\t\t\t\t\t\t\t\t\t\ts = strconv_attribute(s, ch);\r\n\r\n\t\t\t\t\t\t\t\t\t\t\tif (!s) PUGI_IMPL_THROW_ERROR(status_bad_attribute, a->value);\r\n\r\n\t\t\t\t\t\t\t\t\t\t\t// After this line the loop continues from the start;\r\n\t\t\t\t\t\t\t\t\t\t\t// Whitespaces, / and > are ok, symbols and EOF are wrong,\r\n\t\t\t\t\t\t\t\t\t\t\t// everything else will be detected\r\n\t\t\t\t\t\t\t\t\t\t\tif (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) PUGI_IMPL_THROW_ERROR(status_bad_attribute, s);\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\telse PUGI_IMPL_THROW_ERROR(status_bad_attribute, s);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\telse PUGI_IMPL_THROW_ERROR(status_bad_attribute, s);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\telse if (*s == '/')\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t++s;\r\n\r\n\t\t\t\t\t\t\t\t\tif (*s == '>')\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tPUGI_IMPL_POPNODE();\r\n\t\t\t\t\t\t\t\t\t\ts++;\r\n\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\telse if (*s == 0 && endch == '>')\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tPUGI_IMPL_POPNODE();\r\n\t\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\telse PUGI_IMPL_THROW_ERROR(status_bad_start_element, s);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\telse if (*s == '>')\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t++s;\r\n\r\n\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\telse if (*s == 0 && endch == '>')\r\n\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\telse PUGI_IMPL_THROW_ERROR(status_bad_start_element, s);\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t// !!!\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (ch == '/') // '<#.../'\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (!PUGI_IMPL_ENDSWITH(*s, '>')) PUGI_IMPL_THROW_ERROR(status_bad_start_element, s);\r\n\r\n\t\t\t\t\t\t\tPUGI_IMPL_POPNODE(); // Pop.\r\n\r\n\t\t\t\t\t\t\ts += (*s == '>');\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (ch == 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t// we stepped over null terminator, backtrack & handle closing tag\r\n\t\t\t\t\t\t\t--s;\r\n\r\n\t\t\t\t\t\t\tif (endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_start_element, s);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse PUGI_IMPL_THROW_ERROR(status_bad_start_element, s);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (*s == '/')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t++s;\r\n\r\n\t\t\t\t\t\tmark = s;\r\n\r\n\t\t\t\t\t\tchar_t* name = cursor->name;\r\n\t\t\t\t\t\tif (!name) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark);\r\n\r\n\t\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPE(*s, ct_symbol))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (*s++ != *name++) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark);\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tif (*name)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (*s == 0 && name[0] == endch && name[1] == 0) PUGI_IMPL_THROW_ERROR(status_bad_end_element, s);\r\n\t\t\t\t\t\t\telse PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark);\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tPUGI_IMPL_POPNODE(); // Pop.\r\n\r\n\t\t\t\t\t\tPUGI_IMPL_SKIPWS();\r\n\r\n\t\t\t\t\t\tif (*s == 0)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_end_element, s);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (*s != '>') PUGI_IMPL_THROW_ERROR(status_bad_end_element, s);\r\n\t\t\t\t\t\t\t++s;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (*s == '?') // '<?...'\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ts = parse_question(s, cursor, optmsk, endch);\r\n\t\t\t\t\t\tif (!s) return s;\r\n\r\n\t\t\t\t\t\tassert(cursor);\r\n\t\t\t\t\t\tif (PUGI_IMPL_NODETYPE(cursor) == node_declaration) goto LOC_ATTRIBUTES;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (*s == '!') // '<!...'\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ts = parse_exclamation(s, cursor, optmsk, endch);\r\n\t\t\t\t\t\tif (!s) return s;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (*s == 0 && endch == '?') PUGI_IMPL_THROW_ERROR(status_bad_pi, s);\r\n\t\t\t\t\telse PUGI_IMPL_THROW_ERROR(status_unrecognized_tag, s);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tmark = s; // Save this offset while searching for a terminator.\r\n\r\n\t\t\t\t\tPUGI_IMPL_SKIPWS(); // Eat whitespace if no genuine PCDATA here.\r\n\r\n\t\t\t\t\tif (*s == '<' || !*s)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// We skipped some whitespace characters because otherwise we would take the tag branch instead of PCDATA one\r\n\t\t\t\t\t\tassert(mark != s);\r\n\r\n\t\t\t\t\t\tif (!PUGI_IMPL_OPTSET(parse_ws_pcdata | parse_ws_pcdata_single) || PUGI_IMPL_OPTSET(parse_trim_pcdata))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (PUGI_IMPL_OPTSET(parse_ws_pcdata_single))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (s[0] != '<' || s[1] != '/' || cursor->first_child) continue;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (!PUGI_IMPL_OPTSET(parse_trim_pcdata))\r\n\t\t\t\t\t\ts = mark;\r\n\r\n\t\t\t\t\tif (cursor->parent || PUGI_IMPL_OPTSET(parse_fragment))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tchar_t* parsed_pcdata = s;\r\n\r\n\t\t\t\t\t\ts = strconv_pcdata(s);\r\n\r\n\t\t\t\t\t\tif (PUGI_IMPL_OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcursor->value = parsed_pcdata; // Save the offset.\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (PUGI_IMPL_OPTSET(parse_merge_pcdata) && cursor->first_child && PUGI_IMPL_NODETYPE(cursor->first_child->prev_sibling_c) == node_pcdata)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tassert(merged_pcdata >= cursor->first_child->prev_sibling_c->value);\r\n\r\n\t\t\t\t\t\t\t// Catch up to the end of last parsed value; only needed for the first fragment.\r\n\t\t\t\t\t\t\tmerged_pcdata += strlength(merged_pcdata);\r\n\r\n\t\t\t\t\t\t\tsize_t length = strlength(parsed_pcdata);\r\n\r\n\t\t\t\t\t\t\t// Must use memmove instead of memcpy as this move may overlap\r\n\t\t\t\t\t\t\tmemmove(merged_pcdata, parsed_pcdata, (length + 1) * sizeof(char_t));\r\n\t\t\t\t\t\t\tmerged_pcdata += length;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\txml_node_struct* prev_cursor = cursor;\r\n\t\t\t\t\t\t\tPUGI_IMPL_PUSHNODE(node_pcdata); // Append a new node on the tree.\r\n\r\n\t\t\t\t\t\t\tcursor->value = parsed_pcdata; // Save the offset.\r\n\t\t\t\t\t\t\tmerged_pcdata = parsed_pcdata; // Used for parse_merge_pcdata above, cheaper to save unconditionally\r\n\r\n\t\t\t\t\t\t\tcursor = prev_cursor; // Pop since this is a standalone.\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tif (!*s) break;\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tPUGI_IMPL_SCANFOR(*s == '<'); // '...<'\r\n\t\t\t\t\t\tif (!*s) break;\r\n\r\n\t\t\t\t\t\t++s;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t// We're after '<'\r\n\t\t\t\t\tgoto LOC_TAG;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// check that last tag is closed\r\n\t\t\tif (cursor != root) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, s);\r\n\r\n\t\t\treturn s;\r\n\t\t}\r\n\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\tstatic char_t* parse_skip_bom(char_t* s)\r\n\t\t{\r\n\t\t\tunsigned int bom = 0xfeff;\r\n\t\t\treturn (s[0] == static_cast<wchar_t>(bom)) ? s + 1 : s;\r\n\t\t}\r\n\t#else\r\n\t\tstatic char_t* parse_skip_bom(char_t* s)\r\n\t\t{\r\n\t\t\treturn (s[0] == '\\xef' && s[1] == '\\xbb' && s[2] == '\\xbf') ? s + 3 : s;\r\n\t\t}\r\n\t#endif\r\n\r\n\t\tstatic bool has_element_node_siblings(xml_node_struct* node)\r\n\t\t{\r\n\t\t\twhile (node)\r\n\t\t\t{\r\n\t\t\t\tif (PUGI_IMPL_NODETYPE(node) == node_element) return true;\r\n\r\n\t\t\t\tnode = node->next_sibling;\r\n\t\t\t}\r\n\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tstatic xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk)\r\n\t\t{\r\n\t\t\t// early-out for empty documents\r\n\t\t\tif (length == 0)\r\n\t\t\t\treturn make_parse_result(PUGI_IMPL_OPTSET(parse_fragment) ? status_ok : status_no_document_element);\r\n\r\n\t\t\t// get last child of the root before parsing\r\n\t\t\txml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0;\r\n\r\n\t\t\t// create parser on stack\r\n\t\t\txml_parser parser(static_cast<xml_allocator*>(xmldoc));\r\n\r\n\t\t\t// save last character and make buffer zero-terminated (speeds up parsing)\r\n\t\t\tchar_t endch = buffer[length - 1];\r\n\t\t\tbuffer[length - 1] = 0;\r\n\r\n\t\t\t// skip BOM to make sure it does not end up as part of parse output\r\n\t\t\tchar_t* buffer_data = parse_skip_bom(buffer);\r\n\r\n\t\t\t// perform actual parsing\r\n\t\t\tparser.parse_tree(buffer_data, root, optmsk, endch);\r\n\r\n\t\t\txml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0);\r\n\t\t\tassert(result.offset >= 0 && static_cast<size_t>(result.offset) <= length);\r\n\r\n\t\t\tif (result)\r\n\t\t\t{\r\n\t\t\t\t// since we removed last character, we have to handle the only possible false positive (stray <)\r\n\t\t\t\tif (endch == '<')\r\n\t\t\t\t\treturn make_parse_result(status_unrecognized_tag, length - 1);\r\n\r\n\t\t\t\t// check if there are any element nodes parsed\r\n\t\t\t\txml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child + 0;\r\n\r\n\t\t\t\tif (!PUGI_IMPL_OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed))\r\n\t\t\t\t\treturn make_parse_result(status_no_document_element, length - 1);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// roll back offset if it occurs on a null terminator in the source buffer\r\n\t\t\t\tif (result.offset > 0 && static_cast<size_t>(result.offset) == length - 1 && endch == 0)\r\n\t\t\t\t\tresult.offset--;\r\n\t\t\t}\r\n\r\n\t\t\treturn result;\r\n\t\t}\r\n\t};\r\n\r\n\t// Output facilities\r\n\tPUGI_IMPL_FN xml_encoding get_write_native_encoding()\r\n\t{\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\treturn get_wchar_encoding();\r\n\t#else\r\n\t\treturn encoding_utf8;\r\n\t#endif\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_encoding get_write_encoding(xml_encoding encoding)\r\n\t{\r\n\t\t// replace wchar encoding with utf implementation\r\n\t\tif (encoding == encoding_wchar) return get_wchar_encoding();\r\n\r\n\t\t// replace utf16 encoding with utf16 with specific endianness\r\n\t\tif (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;\r\n\r\n\t\t// replace utf32 encoding with utf32 with specific endianness\r\n\t\tif (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;\r\n\r\n\t\t// only do autodetection if no explicit encoding is requested\r\n\t\tif (encoding != encoding_auto) return encoding;\r\n\r\n\t\t// assume utf8 encoding\r\n\t\treturn encoding_utf8;\r\n\t}\r\n\r\n\ttemplate <typename D, typename T> PUGI_IMPL_FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T)\r\n\t{\r\n\t\tPUGI_IMPL_STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type));\r\n\r\n\t\ttypename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T());\r\n\r\n\t\treturn static_cast<size_t>(end - dest) * sizeof(*dest);\r\n\t}\r\n\r\n\ttemplate <typename D, typename T> PUGI_IMPL_FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap)\r\n\t{\r\n\t\tPUGI_IMPL_STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type));\r\n\r\n\t\ttypename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T());\r\n\r\n\t\tif (opt_swap)\r\n\t\t{\r\n\t\t\tfor (typename T::value_type i = dest; i != end; ++i)\r\n\t\t\t\t*i = endian_swap(*i);\r\n\t\t}\r\n\r\n\t\treturn static_cast<size_t>(end - dest) * sizeof(*dest);\r\n\t}\r\n\r\n#ifdef PUGIXML_WCHAR_MODE\r\n\tPUGI_IMPL_FN size_t get_valid_length(const char_t* data, size_t length)\r\n\t{\r\n\t\tif (length < 1) return 0;\r\n\r\n\t\t// discard last character if it's the lead of a surrogate pair\r\n\t\treturn (sizeof(wchar_t) == 2 && static_cast<unsigned int>(static_cast<uint16_t>(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)\r\n\t{\r\n\t\t// only endian-swapping is required\r\n\t\tif (need_endian_swap_utf(encoding, get_wchar_encoding()))\r\n\t\t{\r\n\t\t\tconvert_wchar_endian_swap(r_char, data, length);\r\n\r\n\t\t\treturn length * sizeof(char_t);\r\n\t\t}\r\n\r\n\t\t// convert to utf8\r\n\t\tif (encoding == encoding_utf8)\r\n\t\t\treturn convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer());\r\n\r\n\t\t// convert to utf16\r\n\t\tif (encoding == encoding_utf16_be || encoding == encoding_utf16_le)\r\n\t\t{\r\n\t\t\txml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;\r\n\r\n\t\t\treturn convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding);\r\n\t\t}\r\n\r\n\t\t// convert to utf32\r\n\t\tif (encoding == encoding_utf32_be || encoding == encoding_utf32_le)\r\n\t\t{\r\n\t\t\txml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;\r\n\r\n\t\t\treturn convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding);\r\n\t\t}\r\n\r\n\t\t// convert to latin1\r\n\t\tif (encoding == encoding_latin1)\r\n\t\t\treturn convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer());\r\n\r\n\t\tassert(false && \"Invalid encoding\"); // unreachable\r\n\t\treturn 0;\r\n\t}\r\n#else\r\n\tPUGI_IMPL_FN size_t get_valid_length(const char_t* data, size_t length)\r\n\t{\r\n\t\tif (length < 5) return 0;\r\n\r\n\t\tfor (size_t i = 1; i <= 4; ++i)\r\n\t\t{\r\n\t\t\tuint8_t ch = static_cast<uint8_t>(data[length - i]);\r\n\r\n\t\t\t// either a standalone character or a leading one\r\n\t\t\tif ((ch & 0xc0) != 0x80) return length - i;\r\n\t\t}\r\n\r\n\t\t// there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk\r\n\t\treturn length;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)\r\n\t{\r\n\t\tif (encoding == encoding_utf16_be || encoding == encoding_utf16_le)\r\n\t\t{\r\n\t\t\txml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;\r\n\r\n\t\t\treturn convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding);\r\n\t\t}\r\n\r\n\t\tif (encoding == encoding_utf32_be || encoding == encoding_utf32_le)\r\n\t\t{\r\n\t\t\txml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;\r\n\r\n\t\t\treturn convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding);\r\n\t\t}\r\n\r\n\t\tif (encoding == encoding_latin1)\r\n\t\t\treturn convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer());\r\n\r\n\t\tassert(false && \"Invalid encoding\"); // unreachable\r\n\t\treturn 0;\r\n\t}\r\n#endif\r\n\r\n\tclass xml_buffered_writer\r\n\t{\r\n\t\txml_buffered_writer(const xml_buffered_writer&);\r\n\t\txml_buffered_writer& operator=(const xml_buffered_writer&);\r\n\r\n\tpublic:\r\n\t\txml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding))\r\n\t\t{\r\n\t\t\tPUGI_IMPL_STATIC_ASSERT(bufcapacity >= 8);\r\n\t\t}\r\n\r\n\t\tsize_t flush()\r\n\t\t{\r\n\t\t\tflush(buffer, bufsize);\r\n\t\t\tbufsize = 0;\r\n\t\t\treturn 0;\r\n\t\t}\r\n\r\n\t\tvoid flush(const char_t* data, size_t size)\r\n\t\t{\r\n\t\t\tif (size == 0) return;\r\n\r\n\t\t\t// fast path, just write data\r\n\t\t\tif (encoding == get_write_native_encoding())\r\n\t\t\t\twriter.write(data, size * sizeof(char_t));\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// convert chunk\r\n\t\t\t\tsize_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding);\r\n\t\t\t\tassert(result <= sizeof(scratch));\r\n\r\n\t\t\t\t// write data\r\n\t\t\t\twriter.write(scratch.data_u8, result);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvoid write_direct(const char_t* data, size_t length)\r\n\t\t{\r\n\t\t\t// flush the remaining buffer contents\r\n\t\t\tflush();\r\n\r\n\t\t\t// handle large chunks\r\n\t\t\tif (length > bufcapacity)\r\n\t\t\t{\r\n\t\t\t\tif (encoding == get_write_native_encoding())\r\n\t\t\t\t{\r\n\t\t\t\t\t// fast path, can just write data chunk\r\n\t\t\t\t\twriter.write(data, length * sizeof(char_t));\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// need to convert in suitable chunks\r\n\t\t\t\twhile (length > bufcapacity)\r\n\t\t\t\t{\r\n\t\t\t\t\t// get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer\r\n\t\t\t\t\t// and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)\r\n\t\t\t\t\tsize_t chunk_size = get_valid_length(data, bufcapacity);\r\n\t\t\t\t\tassert(chunk_size);\r\n\r\n\t\t\t\t\t// convert chunk and write\r\n\t\t\t\t\tflush(data, chunk_size);\r\n\r\n\t\t\t\t\t// iterate\r\n\t\t\t\t\tdata += chunk_size;\r\n\t\t\t\t\tlength -= chunk_size;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// small tail is copied below\r\n\t\t\t\tbufsize = 0;\r\n\t\t\t}\r\n\r\n\t\t\tmemcpy(buffer + bufsize, data, length * sizeof(char_t));\r\n\t\t\tbufsize += length;\r\n\t\t}\r\n\r\n\t\tvoid write_buffer(const char_t* data, size_t length)\r\n\t\t{\r\n\t\t\tsize_t offset = bufsize;\r\n\r\n\t\t\tif (offset + length <= bufcapacity)\r\n\t\t\t{\r\n\t\t\t\tmemcpy(buffer + offset, data, length * sizeof(char_t));\r\n\t\t\t\tbufsize = offset + length;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\twrite_direct(data, length);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvoid write_string(const char_t* data)\r\n\t\t{\r\n\t\t\t// write the part of the string that fits in the buffer\r\n\t\t\tsize_t offset = bufsize;\r\n\r\n\t\t\twhile (*data && offset < bufcapacity)\r\n\t\t\t\tbuffer[offset++] = *data++;\r\n\r\n\t\t\t// write the rest\r\n\t\t\tif (offset < bufcapacity)\r\n\t\t\t{\r\n\t\t\t\tbufsize = offset;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// backtrack a bit if we have split the codepoint\r\n\t\t\t\tsize_t length = offset - bufsize;\r\n\t\t\t\tsize_t extra = length - get_valid_length(data - length, length);\r\n\r\n\t\t\t\tbufsize = offset - extra;\r\n\r\n\t\t\t\twrite_direct(data - extra, strlength(data) + extra);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvoid write(char_t d0)\r\n\t\t{\r\n\t\t\tsize_t offset = bufsize;\r\n\t\t\tif (offset > bufcapacity - 1) offset = flush();\r\n\r\n\t\t\tbuffer[offset + 0] = d0;\r\n\t\t\tbufsize = offset + 1;\r\n\t\t}\r\n\r\n\t\tvoid write(char_t d0, char_t d1)\r\n\t\t{\r\n\t\t\tsize_t offset = bufsize;\r\n\t\t\tif (offset > bufcapacity - 2) offset = flush();\r\n\r\n\t\t\tbuffer[offset + 0] = d0;\r\n\t\t\tbuffer[offset + 1] = d1;\r\n\t\t\tbufsize = offset + 2;\r\n\t\t}\r\n\r\n\t\tvoid write(char_t d0, char_t d1, char_t d2)\r\n\t\t{\r\n\t\t\tsize_t offset = bufsize;\r\n\t\t\tif (offset > bufcapacity - 3) offset = flush();\r\n\r\n\t\t\tbuffer[offset + 0] = d0;\r\n\t\t\tbuffer[offset + 1] = d1;\r\n\t\t\tbuffer[offset + 2] = d2;\r\n\t\t\tbufsize = offset + 3;\r\n\t\t}\r\n\r\n\t\tvoid write(char_t d0, char_t d1, char_t d2, char_t d3)\r\n\t\t{\r\n\t\t\tsize_t offset = bufsize;\r\n\t\t\tif (offset > bufcapacity - 4) offset = flush();\r\n\r\n\t\t\tbuffer[offset + 0] = d0;\r\n\t\t\tbuffer[offset + 1] = d1;\r\n\t\t\tbuffer[offset + 2] = d2;\r\n\t\t\tbuffer[offset + 3] = d3;\r\n\t\t\tbufsize = offset + 4;\r\n\t\t}\r\n\r\n\t\tvoid write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4)\r\n\t\t{\r\n\t\t\tsize_t offset = bufsize;\r\n\t\t\tif (offset > bufcapacity - 5) offset = flush();\r\n\r\n\t\t\tbuffer[offset + 0] = d0;\r\n\t\t\tbuffer[offset + 1] = d1;\r\n\t\t\tbuffer[offset + 2] = d2;\r\n\t\t\tbuffer[offset + 3] = d3;\r\n\t\t\tbuffer[offset + 4] = d4;\r\n\t\t\tbufsize = offset + 5;\r\n\t\t}\r\n\r\n\t\tvoid write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5)\r\n\t\t{\r\n\t\t\tsize_t offset = bufsize;\r\n\t\t\tif (offset > bufcapacity - 6) offset = flush();\r\n\r\n\t\t\tbuffer[offset + 0] = d0;\r\n\t\t\tbuffer[offset + 1] = d1;\r\n\t\t\tbuffer[offset + 2] = d2;\r\n\t\t\tbuffer[offset + 3] = d3;\r\n\t\t\tbuffer[offset + 4] = d4;\r\n\t\t\tbuffer[offset + 5] = d5;\r\n\t\t\tbufsize = offset + 6;\r\n\t\t}\r\n\r\n\t\t// utf8 maximum expansion: x4 (-> utf32)\r\n\t\t// utf16 maximum expansion: x2 (-> utf32)\r\n\t\t// utf32 maximum expansion: x1\r\n\t\tenum\r\n\t\t{\r\n\t\t\tbufcapacitybytes =\r\n\t\t\t#ifdef PUGIXML_MEMORY_OUTPUT_STACK\r\n\t\t\t\tPUGIXML_MEMORY_OUTPUT_STACK\r\n\t\t\t#else\r\n\t\t\t\t10240\r\n\t\t\t#endif\r\n\t\t\t,\r\n\t\t\tbufcapacity = bufcapacitybytes / (sizeof(char_t) + 4)\r\n\t\t};\r\n\r\n\t\tchar_t buffer[bufcapacity];\r\n\r\n\t\tunion\r\n\t\t{\r\n\t\t\tuint8_t data_u8[4 * bufcapacity];\r\n\t\t\tuint16_t data_u16[2 * bufcapacity];\r\n\t\t\tuint32_t data_u32[bufcapacity];\r\n\t\t\tchar_t data_char[bufcapacity];\r\n\t\t} scratch;\r\n\r\n\t\txml_writer& writer;\r\n\t\tsize_t bufsize;\r\n\t\txml_encoding encoding;\r\n\t};\r\n\r\n\tPUGI_IMPL_FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)\r\n\t{\r\n\t\twhile (*s)\r\n\t\t{\r\n\t\t\tconst char_t* prev = s;\r\n\r\n\t\t\t// While *s is a usual symbol\r\n\t\t\tPUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPEX(ss, type));\r\n\r\n\t\t\twriter.write_buffer(prev, static_cast<size_t>(s - prev));\r\n\r\n\t\t\tswitch (*s)\r\n\t\t\t{\r\n\t\t\t\tcase 0: break;\r\n\t\t\t\tcase '&':\r\n\t\t\t\t\twriter.write('&', 'a', 'm', 'p', ';');\r\n\t\t\t\t\t++s;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase '<':\r\n\t\t\t\t\twriter.write('&', 'l', 't', ';');\r\n\t\t\t\t\t++s;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase '>':\r\n\t\t\t\t\twriter.write('&', 'g', 't', ';');\r\n\t\t\t\t\t++s;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase '\"':\r\n\t\t\t\t\tif (flags & format_attribute_single_quote)\r\n\t\t\t\t\t\twriter.write('\"');\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\twriter.write('&', 'q', 'u', 'o', 't', ';');\r\n\t\t\t\t\t++s;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase '\\'':\r\n\t\t\t\t\tif (flags & format_attribute_single_quote)\r\n\t\t\t\t\t\twriter.write('&', 'a', 'p', 'o', 's', ';');\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\twriter.write('\\'');\r\n\t\t\t\t\t++s;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tdefault: // s is not a usual symbol\r\n\t\t\t\t{\r\n\t\t\t\t\tunsigned int ch = static_cast<unsigned int>(*s++);\r\n\t\t\t\t\tassert(ch < 32);\r\n\r\n\t\t\t\t\tif (!(flags & format_skip_control_chars))\r\n\t\t\t\t\t\twriter.write('&', '#', static_cast<char_t>((ch / 10) + '0'), static_cast<char_t>((ch % 10) + '0'), ';');\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)\r\n\t{\r\n\t\tif (flags & format_no_escapes)\r\n\t\t\twriter.write_string(s);\r\n\t\telse\r\n\t\t\ttext_output_escaped(writer, s, type, flags);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s)\r\n\t{\r\n\t\tdo\r\n\t\t{\r\n\t\t\twriter.write('<', '!', '[', 'C', 'D');\r\n\t\t\twriter.write('A', 'T', 'A', '[');\r\n\r\n\t\t\tconst char_t* prev = s;\r\n\r\n\t\t\t// look for ]]> sequence - we can't output it as is since it terminates CDATA\r\n\t\t\twhile (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s;\r\n\r\n\t\t\t// skip ]] if we stopped at ]]>, > will go to the next CDATA section\r\n\t\t\tif (*s) s += 2;\r\n\r\n\t\t\twriter.write_buffer(prev, static_cast<size_t>(s - prev));\r\n\r\n\t\t\twriter.write(']', ']', '>');\r\n\t\t}\r\n\t\twhile (*s);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth)\r\n\t{\r\n\t\tswitch (indent_length)\r\n\t\t{\r\n\t\tcase 1:\r\n\t\t{\r\n\t\t\tfor (unsigned int i = 0; i < depth; ++i)\r\n\t\t\t\twriter.write(indent[0]);\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tcase 2:\r\n\t\t{\r\n\t\t\tfor (unsigned int i = 0; i < depth; ++i)\r\n\t\t\t\twriter.write(indent[0], indent[1]);\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tcase 3:\r\n\t\t{\r\n\t\t\tfor (unsigned int i = 0; i < depth; ++i)\r\n\t\t\t\twriter.write(indent[0], indent[1], indent[2]);\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tcase 4:\r\n\t\t{\r\n\t\t\tfor (unsigned int i = 0; i < depth; ++i)\r\n\t\t\t\twriter.write(indent[0], indent[1], indent[2], indent[3]);\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tdefault:\r\n\t\t{\r\n\t\t\tfor (unsigned int i = 0; i < depth; ++i)\r\n\t\t\t\twriter.write_buffer(indent, indent_length);\r\n\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void node_output_comment(xml_buffered_writer& writer, const char_t* s)\r\n\t{\r\n\t\twriter.write('<', '!', '-', '-');\r\n\r\n\t\twhile (*s)\r\n\t\t{\r\n\t\t\tconst char_t* prev = s;\r\n\r\n\t\t\t// look for -\\0 or -- sequence - we can't output it since -- is illegal in comment body\r\n\t\t\twhile (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s;\r\n\r\n\t\t\twriter.write_buffer(prev, static_cast<size_t>(s - prev));\r\n\r\n\t\t\tif (*s)\r\n\t\t\t{\r\n\t\t\t\tassert(*s == '-');\r\n\r\n\t\t\t\twriter.write('-', ' ');\r\n\t\t\t\t++s;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\twriter.write('-', '-', '>');\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s)\r\n\t{\r\n\t\twhile (*s)\r\n\t\t{\r\n\t\t\tconst char_t* prev = s;\r\n\r\n\t\t\t// look for ?> sequence - we can't output it since ?> terminates PI\r\n\t\t\twhile (*s && !(s[0] == '?' && s[1] == '>')) ++s;\r\n\r\n\t\t\twriter.write_buffer(prev, static_cast<size_t>(s - prev));\r\n\r\n\t\t\tif (*s)\r\n\t\t\t{\r\n\t\t\t\tassert(s[0] == '?' && s[1] == '>');\r\n\r\n\t\t\t\twriter.write('?', ' ', '>');\r\n\t\t\t\ts += 2;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth)\r\n\t{\r\n\t\tconst char_t* default_name = PUGIXML_TEXT(\":anonymous\");\r\n\t\tconst char_t enquotation_char = (flags & format_attribute_single_quote) ? '\\'' : '\"';\r\n\r\n\t\tfor (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)\r\n\t\t{\r\n\t\t\tif ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes)\r\n\t\t\t{\r\n\t\t\t\twriter.write('\\n');\r\n\r\n\t\t\t\ttext_output_indent(writer, indent, indent_length, depth + 1);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\twriter.write(' ');\r\n\t\t\t}\r\n\r\n\t\t\twriter.write_string(a->name ? a->name + 0 : default_name);\r\n\t\t\twriter.write('=', enquotation_char);\r\n\r\n\t\t\tif (a->value)\r\n\t\t\t\ttext_output(writer, a->value, ctx_special_attr, flags);\r\n\r\n\t\t\twriter.write(enquotation_char);\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth)\r\n\t{\r\n\t\tconst char_t* default_name = PUGIXML_TEXT(\":anonymous\");\r\n\t\tconst char_t* name = node->name ? node->name + 0 : default_name;\r\n\r\n\t\twriter.write('<');\r\n\t\twriter.write_string(name);\r\n\r\n\t\tif (node->first_attribute)\r\n\t\t\tnode_output_attributes(writer, node, indent, indent_length, flags, depth);\r\n\r\n\t\t// element nodes can have value if parse_embed_pcdata was used\r\n\t\tif (!node->value)\r\n\t\t{\r\n\t\t\tif (!node->first_child)\r\n\t\t\t{\r\n\t\t\t\tif (flags & format_no_empty_element_tags)\r\n\t\t\t\t{\r\n\t\t\t\t\twriter.write('>', '<', '/');\r\n\t\t\t\t\twriter.write_string(name);\r\n\t\t\t\t\twriter.write('>');\r\n\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tif ((flags & format_raw) == 0)\r\n\t\t\t\t\t\twriter.write(' ');\r\n\r\n\t\t\t\t\twriter.write('/', '>');\r\n\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\twriter.write('>');\r\n\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twriter.write('>');\r\n\r\n\t\t\ttext_output(writer, node->value, ctx_special_pcdata, flags);\r\n\r\n\t\t\tif (!node->first_child)\r\n\t\t\t{\r\n\t\t\t\twriter.write('<', '/');\r\n\t\t\t\twriter.write_string(name);\r\n\t\t\t\twriter.write('>');\r\n\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node)\r\n\t{\r\n\t\tconst char_t* default_name = PUGIXML_TEXT(\":anonymous\");\r\n\t\tconst char_t* name = node->name ? node->name + 0 : default_name;\r\n\r\n\t\twriter.write('<', '/');\r\n\t\twriter.write_string(name);\r\n\t\twriter.write('>');\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)\r\n\t{\r\n\t\tconst char_t* default_name = PUGIXML_TEXT(\":anonymous\");\r\n\r\n\t\tswitch (PUGI_IMPL_NODETYPE(node))\r\n\t\t{\r\n\t\t\tcase node_pcdata:\r\n\t\t\t\ttext_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(\"\"), ctx_special_pcdata, flags);\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase node_cdata:\r\n\t\t\t\ttext_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT(\"\"));\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase node_comment:\r\n\t\t\t\tnode_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT(\"\"));\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase node_pi:\r\n\t\t\t\twriter.write('<', '?');\r\n\t\t\t\twriter.write_string(node->name ? node->name + 0 : default_name);\r\n\r\n\t\t\t\tif (node->value)\r\n\t\t\t\t{\r\n\t\t\t\t\twriter.write(' ');\r\n\t\t\t\t\tnode_output_pi_value(writer, node->value);\r\n\t\t\t\t}\r\n\r\n\t\t\t\twriter.write('?', '>');\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase node_declaration:\r\n\t\t\t\twriter.write('<', '?');\r\n\t\t\t\twriter.write_string(node->name ? node->name + 0 : default_name);\r\n\t\t\t\tnode_output_attributes(writer, node, PUGIXML_TEXT(\"\"), 0, flags | format_raw, 0);\r\n\t\t\t\twriter.write('?', '>');\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase node_doctype:\r\n\t\t\t\twriter.write('<', '!', 'D', 'O', 'C');\r\n\t\t\t\twriter.write('T', 'Y', 'P', 'E');\r\n\r\n\t\t\t\tif (node->value)\r\n\t\t\t\t{\r\n\t\t\t\t\twriter.write(' ');\r\n\t\t\t\t\twriter.write_string(node->value);\r\n\t\t\t\t}\r\n\r\n\t\t\t\twriter.write('>');\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tassert(false && \"Invalid node type\"); // unreachable\r\n\t\t}\r\n\t}\r\n\r\n\tenum indent_flags_t\r\n\t{\r\n\t\tindent_newline = 1,\r\n\t\tindent_indent = 2\r\n\t};\r\n\r\n\tPUGI_IMPL_FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth)\r\n\t{\r\n\t\tsize_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0;\r\n\t\tunsigned int indent_flags = indent_indent;\r\n\r\n\t\txml_node_struct* node = root;\r\n\r\n\t\tdo\r\n\t\t{\r\n\t\t\tassert(node);\r\n\r\n\t\t\t// begin writing current node\r\n\t\t\tif (PUGI_IMPL_NODETYPE(node) == node_pcdata || PUGI_IMPL_NODETYPE(node) == node_cdata)\r\n\t\t\t{\r\n\t\t\t\tnode_output_simple(writer, node, flags);\r\n\r\n\t\t\t\tindent_flags = 0;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif ((indent_flags & indent_newline) && (flags & format_raw) == 0)\r\n\t\t\t\t\twriter.write('\\n');\r\n\r\n\t\t\t\tif ((indent_flags & indent_indent) && indent_length)\r\n\t\t\t\t\ttext_output_indent(writer, indent, indent_length, depth);\r\n\r\n\t\t\t\tif (PUGI_IMPL_NODETYPE(node) == node_element)\r\n\t\t\t\t{\r\n\t\t\t\t\tindent_flags = indent_newline | indent_indent;\r\n\r\n\t\t\t\t\tif (node_output_start(writer, node, indent, indent_length, flags, depth))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// element nodes can have value if parse_embed_pcdata was used\r\n\t\t\t\t\t\tif (node->value)\r\n\t\t\t\t\t\t\tindent_flags = 0;\r\n\r\n\t\t\t\t\t\tnode = node->first_child;\r\n\t\t\t\t\t\tdepth++;\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse if (PUGI_IMPL_NODETYPE(node) == node_document)\r\n\t\t\t\t{\r\n\t\t\t\t\tindent_flags = indent_indent;\r\n\r\n\t\t\t\t\tif (node->first_child)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tnode = node->first_child;\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tnode_output_simple(writer, node, flags);\r\n\r\n\t\t\t\t\tindent_flags = indent_newline | indent_indent;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// continue to the next node\r\n\t\t\twhile (node != root)\r\n\t\t\t{\r\n\t\t\t\tif (node->next_sibling)\r\n\t\t\t\t{\r\n\t\t\t\t\tnode = node->next_sibling;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tnode = node->parent;\r\n\r\n\t\t\t\t// write closing node\r\n\t\t\t\tif (PUGI_IMPL_NODETYPE(node) == node_element)\r\n\t\t\t\t{\r\n\t\t\t\t\tdepth--;\r\n\r\n\t\t\t\t\tif ((indent_flags & indent_newline) && (flags & format_raw) == 0)\r\n\t\t\t\t\t\twriter.write('\\n');\r\n\r\n\t\t\t\t\tif ((indent_flags & indent_indent) && indent_length)\r\n\t\t\t\t\t\ttext_output_indent(writer, indent, indent_length, depth);\r\n\r\n\t\t\t\t\tnode_output_end(writer, node);\r\n\r\n\t\t\t\t\tindent_flags = indent_newline | indent_indent;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\twhile (node != root);\r\n\r\n\t\tif ((indent_flags & indent_newline) && (flags & format_raw) == 0)\r\n\t\t\twriter.write('\\n');\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool has_declaration(xml_node_struct* node)\r\n\t{\r\n\t\tfor (xml_node_struct* child = node->first_child; child; child = child->next_sibling)\r\n\t\t{\r\n\t\t\txml_node_type type = PUGI_IMPL_NODETYPE(child);\r\n\r\n\t\t\tif (type == node_declaration) return true;\r\n\t\t\tif (type == node_element) return false;\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node)\r\n\t{\r\n\t\tfor (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)\r\n\t\t\tif (a == attr)\r\n\t\t\t\treturn true;\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool allow_insert_attribute(xml_node_type parent)\r\n\t{\r\n\t\treturn parent == node_element || parent == node_declaration;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool allow_insert_child(xml_node_type parent, xml_node_type child)\r\n\t{\r\n\t\tif (parent != node_document && parent != node_element) return false;\r\n\t\tif (child == node_document || child == node_null) return false;\r\n\t\tif (parent != node_document && (child == node_declaration || child == node_doctype)) return false;\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool allow_move(xml_node parent, xml_node child)\r\n\t{\r\n\t\t// check that child can be a child of parent\r\n\t\tif (!allow_insert_child(parent.type(), child.type()))\r\n\t\t\treturn false;\r\n\r\n\t\t// check that node is not moved between documents\r\n\t\tif (parent.root() != child.root())\r\n\t\t\treturn false;\r\n\r\n\t\t// check that new parent is not in the child subtree\r\n\t\txml_node cur = parent;\r\n\r\n\t\twhile (cur)\r\n\t\t{\r\n\t\t\tif (cur == child)\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tcur = cur.parent();\r\n\t\t}\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\ttemplate <typename String, typename Header>\r\n\tPUGI_IMPL_FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc)\r\n\t{\r\n\t\tassert(!dest && (header & header_mask) == 0); // copies are performed into fresh nodes\r\n\r\n\t\tif (source)\r\n\t\t{\r\n\t\t\tif (alloc && (source_header & header_mask) == 0)\r\n\t\t\t{\r\n\t\t\t\tdest = source;\r\n\r\n\t\t\t\t// since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared\r\n\t\t\t\theader |= xml_memory_page_contents_shared_mask;\r\n\t\t\t\tsource_header |= xml_memory_page_contents_shared_mask;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\tstrcpy_insitu(dest, header, header_mask, source, strlength(source));\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc)\r\n\t{\r\n\t\tnode_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc);\r\n\t\tnode_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc);\r\n\r\n\t\tfor (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute)\r\n\t\t{\r\n\t\t\txml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn));\r\n\r\n\t\t\tif (da)\r\n\t\t\t{\r\n\t\t\t\tnode_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);\r\n\t\t\t\tnode_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn)\r\n\t{\r\n\t\txml_allocator& alloc = get_allocator(dn);\r\n\t\txml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0;\r\n\r\n\t\tnode_copy_contents(dn, sn, shared_alloc);\r\n\r\n\t\txml_node_struct* dit = dn;\r\n\t\txml_node_struct* sit = sn->first_child;\r\n\r\n\t\twhile (sit && sit != sn)\r\n\t\t{\r\n\t\t\t// loop invariant: dit is inside the subtree rooted at dn\r\n\t\t\tassert(dit);\r\n\r\n\t\t\t// when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop\r\n\t\t\tif (sit != dn)\r\n\t\t\t{\r\n\t\t\t\txml_node_struct* copy = append_new_node(dit, alloc, PUGI_IMPL_NODETYPE(sit));\r\n\r\n\t\t\t\tif (copy)\r\n\t\t\t\t{\r\n\t\t\t\t\tnode_copy_contents(copy, sit, shared_alloc);\r\n\r\n\t\t\t\t\tif (sit->first_child)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tdit = copy;\r\n\t\t\t\t\t\tsit = sit->first_child;\r\n\t\t\t\t\t\tcontinue;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// continue to the next node\r\n\t\t\tdo\r\n\t\t\t{\r\n\t\t\t\tif (sit->next_sibling)\r\n\t\t\t\t{\r\n\t\t\t\t\tsit = sit->next_sibling;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tsit = sit->parent;\r\n\t\t\t\tdit = dit->parent;\r\n\r\n\t\t\t\t// loop invariant: dit is inside the subtree rooted at dn while sit is inside sn\r\n\t\t\t\tassert(sit == sn || dit);\r\n\t\t\t}\r\n\t\t\twhile (sit != sn);\r\n\t\t}\r\n\r\n\t\tassert(!sit || dit == dn->parent);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa)\r\n\t{\r\n\t\txml_allocator& alloc = get_allocator(da);\r\n\t\txml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0;\r\n\r\n\t\tnode_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);\r\n\t\tnode_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);\r\n\t}\r\n\r\n\tinline bool is_text_node(xml_node_struct* node)\r\n\t{\r\n\t\txml_node_type type = PUGI_IMPL_NODETYPE(node);\r\n\r\n\t\treturn type == node_pcdata || type == node_cdata;\r\n\t}\r\n\r\n\t// get value with conversion functions\r\n\ttemplate <typename U> PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv)\r\n\t{\r\n\t\tU result = 0;\r\n\t\tconst char_t* s = value;\r\n\r\n\t\twhile (PUGI_IMPL_IS_CHARTYPE(*s, ct_space))\r\n\t\t\ts++;\r\n\r\n\t\tbool negative = (*s == '-');\r\n\r\n\t\ts += (*s == '+' || *s == '-');\r\n\r\n\t\tbool overflow = false;\r\n\r\n\t\tif (s[0] == '0' && (s[1] | ' ') == 'x')\r\n\t\t{\r\n\t\t\ts += 2;\r\n\r\n\t\t\t// since overflow detection relies on length of the sequence skip leading zeros\r\n\t\t\twhile (*s == '0')\r\n\t\t\t\ts++;\r\n\r\n\t\t\tconst char_t* start = s;\r\n\r\n\t\t\tfor (;;)\r\n\t\t\t{\r\n\t\t\t\tif (static_cast<unsigned>(*s - '0') < 10)\r\n\t\t\t\t\tresult = result * 16 + (*s - '0');\r\n\t\t\t\telse if (static_cast<unsigned>((*s | ' ') - 'a') < 6)\r\n\t\t\t\t\tresult = result * 16 + ((*s | ' ') - 'a' + 10);\r\n\t\t\t\telse\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\ts++;\r\n\t\t\t}\r\n\r\n\t\t\tsize_t digits = static_cast<size_t>(s - start);\r\n\r\n\t\t\toverflow = digits > sizeof(U) * 2;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// since overflow detection relies on length of the sequence skip leading zeros\r\n\t\t\twhile (*s == '0')\r\n\t\t\t\ts++;\r\n\r\n\t\t\tconst char_t* start = s;\r\n\r\n\t\t\tfor (;;)\r\n\t\t\t{\r\n\t\t\t\tif (static_cast<unsigned>(*s - '0') < 10)\r\n\t\t\t\t\tresult = result * 10 + (*s - '0');\r\n\t\t\t\telse\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\ts++;\r\n\t\t\t}\r\n\r\n\t\t\tsize_t digits = static_cast<size_t>(s - start);\r\n\r\n\t\t\tPUGI_IMPL_STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2);\r\n\r\n\t\t\tconst size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5;\r\n\t\t\tconst char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6';\r\n\t\t\tconst size_t high_bit = sizeof(U) * 8 - 1;\r\n\r\n\t\t\toverflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit)));\r\n\t\t}\r\n\r\n\t\tif (negative)\r\n\t\t{\r\n\t\t\t// Workaround for crayc++ CC-3059: Expected no overflow in routine.\r\n\t\t#ifdef _CRAYC\r\n\t\t\treturn (overflow || result > ~minv + 1) ? minv : ~result + 1;\r\n\t\t#else\r\n\t\t\treturn (overflow || result > 0 - minv) ? minv : 0 - result;\r\n\t\t#endif\r\n\t\t}\r\n\t\telse\r\n\t\t\treturn (overflow || result > maxv) ? maxv : result;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN int get_value_int(const char_t* value)\r\n\t{\r\n\t\treturn string_to_integer<unsigned int>(value, static_cast<unsigned int>(INT_MIN), INT_MAX);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN unsigned int get_value_uint(const char_t* value)\r\n\t{\r\n\t\treturn string_to_integer<unsigned int>(value, 0, UINT_MAX);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN double get_value_double(const char_t* value)\r\n\t{\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\treturn wcstod(value, 0);\r\n\t#else\r\n\t\treturn strtod(value, 0);\r\n\t#endif\r\n\t}\r\n\r\n\tPUGI_IMPL_FN float get_value_float(const char_t* value)\r\n\t{\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\treturn static_cast<float>(wcstod(value, 0));\r\n\t#else\r\n\t\treturn static_cast<float>(strtod(value, 0));\r\n\t#endif\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool get_value_bool(const char_t* value)\r\n\t{\r\n\t\t// only look at first char\r\n\t\tchar_t first = *value;\r\n\r\n\t\t// 1*, t* (true), T* (True), y* (yes), Y* (YES)\r\n\t\treturn (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y');\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_LONG_LONG\r\n\tPUGI_IMPL_FN long long get_value_llong(const char_t* value)\r\n\t{\r\n\t\treturn string_to_integer<unsigned long long>(value, static_cast<unsigned long long>(LLONG_MIN), LLONG_MAX);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN unsigned long long get_value_ullong(const char_t* value)\r\n\t{\r\n\t\treturn string_to_integer<unsigned long long>(value, 0, ULLONG_MAX);\r\n\t}\r\n#endif\r\n\r\n\ttemplate <typename U> PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative)\r\n\t{\r\n\t\tchar_t* result = end - 1;\r\n\t\tU rest = negative ? 0 - value : value;\r\n\r\n\t\tdo\r\n\t\t{\r\n\t\t\t*result-- = static_cast<char_t>('0' + (rest % 10));\r\n\t\t\trest /= 10;\r\n\t\t}\r\n\t\twhile (rest);\r\n\r\n\t\tassert(result >= begin);\r\n\t\t(void)begin;\r\n\r\n\t\t*result = '-';\r\n\r\n\t\treturn result + !negative;\r\n\t}\r\n\r\n\t// set value with conversion functions\r\n\ttemplate <typename String, typename Header>\r\n\tPUGI_IMPL_FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf)\r\n\t{\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\tchar_t wbuf[128];\r\n\t\tassert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0]));\r\n\r\n\t\tsize_t offset = 0;\r\n\t\tfor (; buf[offset]; ++offset) wbuf[offset] = buf[offset];\r\n\r\n\t\treturn strcpy_insitu(dest, header, header_mask, wbuf, offset);\r\n\t#else\r\n\t\treturn strcpy_insitu(dest, header, header_mask, buf, strlen(buf));\r\n\t#endif\r\n\t}\r\n\r\n\ttemplate <typename U, typename String, typename Header>\r\n\tPUGI_IMPL_FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative)\r\n\t{\r\n\t\tchar_t buf[64];\r\n\t\tchar_t* end = buf + sizeof(buf) / sizeof(buf[0]);\r\n\t\tchar_t* begin = integer_to_string(buf, end, value, negative);\r\n\r\n\t\treturn strcpy_insitu(dest, header, header_mask, begin, end - begin);\r\n\t}\r\n\r\n\ttemplate <typename String, typename Header>\r\n\tPUGI_IMPL_FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value, int precision)\r\n\t{\r\n\t\tchar buf[128];\r\n\t\tPUGI_IMPL_SNPRINTF(buf, \"%.*g\", precision, double(value));\r\n\r\n\t\treturn set_value_ascii(dest, header, header_mask, buf);\r\n\t}\r\n\r\n\ttemplate <typename String, typename Header>\r\n\tPUGI_IMPL_FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value, int precision)\r\n\t{\r\n\t\tchar buf[128];\r\n\t\tPUGI_IMPL_SNPRINTF(buf, \"%.*g\", precision, value);\r\n\r\n\t\treturn set_value_ascii(dest, header, header_mask, buf);\r\n\t}\r\n\r\n\ttemplate <typename String, typename Header>\r\n\tPUGI_IMPL_FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value)\r\n\t{\r\n\t\treturn strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT(\"true\") : PUGIXML_TEXT(\"false\"), value ? 4 : 5);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer)\r\n\t{\r\n\t\t// check input buffer\r\n\t\tif (!contents && size) return make_parse_result(status_io_error);\r\n\r\n\t\t// get actual encoding\r\n\t\txml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);\r\n\r\n\t\t// if convert_buffer below throws bad_alloc, we still need to deallocate contents if we own it\r\n\t\tauto_deleter<void> contents_guard(own ? contents : 0, xml_memory::deallocate);\r\n\r\n\t\t// get private buffer\r\n\t\tchar_t* buffer = 0;\r\n\t\tsize_t length = 0;\r\n\r\n\t\t// coverity[var_deref_model]\r\n\t\tif (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory);\r\n\r\n\t\t// after this we either deallocate contents (below) or hold on to it via doc->buffer, so we don't need to guard it\r\n\t\tcontents_guard.release();\r\n\r\n\t\t// delete original buffer if we performed a conversion\r\n\t\tif (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);\r\n\r\n\t\t// grab onto buffer if it's our buffer, user is responsible for deallocating contents himself\r\n\t\tif (own || buffer != contents) *out_buffer = buffer;\r\n\r\n\t\t// store buffer for offset_debug\r\n\t\tdoc->buffer = buffer;\r\n\r\n\t\t// parse\r\n\t\txml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options);\r\n\r\n\t\t// remember encoding\r\n\t\tres.encoding = buffer_encoding;\r\n\r\n\t\treturn res;\r\n\t}\r\n\r\n\t// we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick\r\n\tPUGI_IMPL_FN xml_parse_status get_file_size(FILE* file, size_t& out_result)\r\n\t{\r\n\t#if defined(__linux__) || defined(__APPLE__)\r\n\t\t// this simultaneously retrieves the file size and file mode (to guard against loading non-files)\r\n\t\tstruct stat st;\r\n\t\tif (fstat(fileno(file), &st) != 0) return status_io_error;\r\n\r\n\t\t// anything that's not a regular file doesn't have a coherent length\r\n\t\tif (!S_ISREG(st.st_mode)) return status_io_error;\r\n\r\n\t\ttypedef off_t length_type;\r\n\t\tlength_type length = st.st_size;\r\n\t#elif defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400\r\n\t\t// there are 64-bit versions of fseek/ftell, let's use them\r\n\t\ttypedef __int64 length_type;\r\n\r\n\t\t_fseeki64(file, 0, SEEK_END);\r\n\t\tlength_type length = _ftelli64(file);\r\n\t\t_fseeki64(file, 0, SEEK_SET);\r\n\t#elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))\r\n\t\t// there are 64-bit versions of fseek/ftell, let's use them\r\n\t\ttypedef off64_t length_type;\r\n\r\n\t\tfseeko64(file, 0, SEEK_END);\r\n\t\tlength_type length = ftello64(file);\r\n\t\tfseeko64(file, 0, SEEK_SET);\r\n\t#else\r\n\t\t// if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway.\r\n\t\ttypedef long length_type;\r\n\r\n\t\tfseek(file, 0, SEEK_END);\r\n\t\tlength_type length = ftell(file);\r\n\t\tfseek(file, 0, SEEK_SET);\r\n\t#endif\r\n\r\n\t\t// check for I/O errors\r\n\t\tif (length < 0) return status_io_error;\r\n\r\n\t\t// check for overflow\r\n\t\tsize_t result = static_cast<size_t>(length);\r\n\r\n\t\tif (static_cast<length_type>(result) != length) return status_out_of_memory;\r\n\r\n\t\t// finalize\r\n\t\tout_result = result;\r\n\r\n\t\treturn status_ok;\r\n\t}\r\n\r\n\t// This function assumes that buffer has extra sizeof(char_t) writable bytes after size\r\n\tPUGI_IMPL_FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding)\r\n\t{\r\n\t\t// We only need to zero-terminate if encoding conversion does not do it for us\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\txml_encoding wchar_encoding = get_wchar_encoding();\r\n\r\n\t\tif (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding))\r\n\t\t{\r\n\t\t\tsize_t length = size / sizeof(char_t);\r\n\r\n\t\t\tstatic_cast<char_t*>(buffer)[length] = 0;\r\n\t\t\treturn (length + 1) * sizeof(char_t);\r\n\t\t}\r\n\t#else\r\n\t\tif (encoding == encoding_utf8)\r\n\t\t{\r\n\t\t\tstatic_cast<char*>(buffer)[size] = 0;\r\n\t\t\treturn size + 1;\r\n\t\t}\r\n\t#endif\r\n\r\n\t\treturn size;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer)\r\n\t{\r\n\t\tif (!file) return make_parse_result(status_file_not_found);\r\n\r\n\t\t// get file size (can result in I/O errors)\r\n\t\tsize_t size = 0;\r\n\t\txml_parse_status size_status = get_file_size(file, size);\r\n\t\tif (size_status != status_ok) return make_parse_result(size_status);\r\n\r\n\t\tsize_t max_suffix_size = sizeof(char_t);\r\n\r\n\t\t// allocate buffer for the whole file\r\n\t\tchar* contents = static_cast<char*>(xml_memory::allocate(size + max_suffix_size));\r\n\t\tif (!contents) return make_parse_result(status_out_of_memory);\r\n\r\n\t\t// read file in memory\r\n\t\tsize_t read_size = fread(contents, 1, size, file);\r\n\r\n\t\tif (read_size != size)\r\n\t\t{\r\n\t\t\txml_memory::deallocate(contents);\r\n\t\t\treturn make_parse_result(status_io_error);\r\n\t\t}\r\n\r\n\t\txml_encoding real_encoding = get_buffer_encoding(encoding, contents, size);\r\n\r\n\t\treturn load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void close_file(FILE* file)\r\n\t{\r\n\t\tfclose(file);\r\n\t}\r\n\r\n#ifndef PUGIXML_NO_STL\r\n\ttemplate <typename T> struct xml_stream_chunk\r\n\t{\r\n\t\tstatic xml_stream_chunk* create()\r\n\t\t{\r\n\t\t\tvoid* memory = xml_memory::allocate(sizeof(xml_stream_chunk));\r\n\t\t\tif (!memory) return 0;\r\n\r\n\t\t\treturn new (memory) xml_stream_chunk();\r\n\t\t}\r\n\r\n\t\tstatic void destroy(xml_stream_chunk* chunk)\r\n\t\t{\r\n\t\t\t// free chunk chain\r\n\t\t\twhile (chunk)\r\n\t\t\t{\r\n\t\t\t\txml_stream_chunk* next_ = chunk->next;\r\n\r\n\t\t\t\txml_memory::deallocate(chunk);\r\n\r\n\t\t\t\tchunk = next_;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\txml_stream_chunk(): next(0), size(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\txml_stream_chunk* next;\r\n\t\tsize_t size;\r\n\r\n\t\tT data[xml_memory_page_size / sizeof(T)];\r\n\t};\r\n\r\n\ttemplate <typename T> PUGI_IMPL_FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)\r\n\t{\r\n\t\tauto_deleter<xml_stream_chunk<T> > chunks(0, xml_stream_chunk<T>::destroy);\r\n\r\n\t\t// read file to a chunk list\r\n\t\tsize_t total = 0;\r\n\t\txml_stream_chunk<T>* last = 0;\r\n\r\n\t\twhile (!stream.eof())\r\n\t\t{\r\n\t\t\t// allocate new chunk\r\n\t\t\txml_stream_chunk<T>* chunk = xml_stream_chunk<T>::create();\r\n\t\t\tif (!chunk) return status_out_of_memory;\r\n\r\n\t\t\t// append chunk to list\r\n\t\t\tif (last) last = last->next = chunk;\r\n\t\t\telse chunks.data = last = chunk;\r\n\r\n\t\t\t// read data to chunk\r\n\t\t\tstream.read(chunk->data, static_cast<std::streamsize>(sizeof(chunk->data) / sizeof(T)));\r\n\t\t\tchunk->size = static_cast<size_t>(stream.gcount()) * sizeof(T);\r\n\r\n\t\t\t// read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors\r\n\t\t\tif (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;\r\n\r\n\t\t\t// guard against huge files (chunk size is small enough to make this overflow check work)\r\n\t\t\tif (total + chunk->size < total) return status_out_of_memory;\r\n\t\t\ttotal += chunk->size;\r\n\t\t}\r\n\r\n\t\tsize_t max_suffix_size = sizeof(char_t);\r\n\r\n\t\t// copy chunk list to a contiguous buffer\r\n\t\tchar* buffer = static_cast<char*>(xml_memory::allocate(total + max_suffix_size));\r\n\t\tif (!buffer) return status_out_of_memory;\r\n\r\n\t\tchar* write = buffer;\r\n\r\n\t\tfor (xml_stream_chunk<T>* chunk = chunks.data; chunk; chunk = chunk->next)\r\n\t\t{\r\n\t\t\tassert(write + chunk->size <= buffer + total);\r\n\t\t\tmemcpy(write, chunk->data, chunk->size);\r\n\t\t\twrite += chunk->size;\r\n\t\t}\r\n\r\n\t\tassert(write == buffer + total);\r\n\r\n\t\t// return buffer\r\n\t\t*out_buffer = buffer;\r\n\t\t*out_size = total;\r\n\r\n\t\treturn status_ok;\r\n\t}\r\n\r\n\ttemplate <typename T> PUGI_IMPL_FN xml_parse_status load_stream_data_seek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)\r\n\t{\r\n\t\t// get length of remaining data in stream\r\n\t\ttypename std::basic_istream<T>::pos_type pos = stream.tellg();\r\n\t\tstream.seekg(0, std::ios::end);\r\n\t\tstd::streamoff length = stream.tellg() - pos;\r\n\t\tstream.seekg(pos);\r\n\r\n\t\tif (stream.fail() || pos < 0) return status_io_error;\r\n\r\n\t\t// guard against huge files\r\n\t\tsize_t read_length = static_cast<size_t>(length);\r\n\r\n\t\tif (static_cast<std::streamsize>(read_length) != length || length < 0) return status_out_of_memory;\r\n\r\n\t\tsize_t max_suffix_size = sizeof(char_t);\r\n\r\n\t\t// read stream data into memory (guard against stream exceptions with buffer holder)\r\n\t\tauto_deleter<void> buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate);\r\n\t\tif (!buffer.data) return status_out_of_memory;\r\n\r\n\t\tstream.read(static_cast<T*>(buffer.data), static_cast<std::streamsize>(read_length));\r\n\r\n\t\t// read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors\r\n\t\tif (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;\r\n\r\n\t\t// return buffer\r\n\t\tsize_t actual_length = static_cast<size_t>(stream.gcount());\r\n\t\tassert(actual_length <= read_length);\r\n\r\n\t\t*out_buffer = buffer.release();\r\n\t\t*out_size = actual_length * sizeof(T);\r\n\r\n\t\treturn status_ok;\r\n\t}\r\n\r\n\ttemplate <typename T> PUGI_IMPL_FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream<T>& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer)\r\n\t{\r\n\t\tvoid* buffer = 0;\r\n\t\tsize_t size = 0;\r\n\t\txml_parse_status status = status_ok;\r\n\r\n\t\t// if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits)\r\n\t\tif (stream.fail()) return make_parse_result(status_io_error);\r\n\r\n\t\t// load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory)\r\n\t\tif (stream.tellg() < 0)\r\n\t\t{\r\n\t\t\tstream.clear(); // clear error flags that could be set by a failing tellg\r\n\t\t\tstatus = load_stream_data_noseek(stream, &buffer, &size);\r\n\t\t}\r\n\t\telse\r\n\t\t\tstatus = load_stream_data_seek(stream, &buffer, &size);\r\n\r\n\t\tif (status != status_ok) return make_parse_result(status);\r\n\r\n\t\txml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size);\r\n\r\n\t\treturn load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer);\r\n\t}\r\n#endif\r\n\r\n#if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)))\r\n\tPUGI_IMPL_FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)\r\n\t{\r\n#if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400\r\n\t\tFILE* file = 0;\r\n\t\treturn _wfopen_s(&file, path, mode) == 0 ? file : 0;\r\n#else\r\n\t\treturn _wfopen(path, mode);\r\n#endif\r\n\t}\r\n#else\r\n\tPUGI_IMPL_FN char* convert_path_heap(const wchar_t* str)\r\n\t{\r\n\t\tassert(str);\r\n\r\n\t\t// first pass: get length in utf8 characters\r\n\t\tsize_t length = strlength_wide(str);\r\n\t\tsize_t size = as_utf8_begin(str, length);\r\n\r\n\t\t// allocate resulting string\r\n\t\tchar* result = static_cast<char*>(xml_memory::allocate(size + 1));\r\n\t\tif (!result) return 0;\r\n\r\n\t\t// second pass: convert to utf8\r\n\t\tas_utf8_end(result, size, str, length);\r\n\r\n\t\t// zero-terminate\r\n\t\tresult[size] = 0;\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)\r\n\t{\r\n\t\t// there is no standard function to open wide paths, so our best bet is to try utf8 path\r\n\t\tchar* path_utf8 = convert_path_heap(path);\r\n\t\tif (!path_utf8) return 0;\r\n\r\n\t\t// convert mode to ASCII (we mirror _wfopen interface)\r\n\t\tchar mode_ascii[4] = {0};\r\n\t\tfor (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast<char>(mode[i]);\r\n\r\n\t\t// try to open the utf8 path\r\n\t\tFILE* result = fopen(path_utf8, mode_ascii);\r\n\r\n\t\t// free dummy buffer\r\n\t\txml_memory::deallocate(path_utf8);\r\n\r\n\t\treturn result;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN FILE* open_file(const char* path, const char* mode)\r\n\t{\r\n#if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400\r\n\t\tFILE* file = 0;\r\n\t\treturn fopen_s(&file, path, mode) == 0 ? file : 0;\r\n#else\r\n\t\treturn fopen(path, mode);\r\n#endif\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding)\r\n\t{\r\n\t\tif (!file) return false;\r\n\r\n\t\txml_writer_file writer(file);\r\n\t\tdoc.save(writer, indent, flags, encoding);\r\n\r\n\t\treturn fflush(file) == 0 && ferror(file) == 0;\r\n\t}\r\n\r\n\tstruct name_null_sentry\r\n\t{\r\n\t\txml_node_struct* node;\r\n\t\tchar_t* name;\r\n\r\n\t\tname_null_sentry(xml_node_struct* node_): node(node_), name(node_->name)\r\n\t\t{\r\n\t\t\tnode->name = 0;\r\n\t\t}\r\n\r\n\t\t~name_null_sentry()\r\n\t\t{\r\n\t\t\tnode->name = name;\r\n\t\t}\r\n\t};\r\nPUGI_IMPL_NS_END\r\n\r\nnamespace pugi\r\n{\r\n\tPUGI_IMPL_FN xml_writer::~xml_writer()\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_writer_file::xml_writer_file(void* file_): file(file_)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xml_writer_file::write(const void* data, size_t size)\r\n\t{\r\n\t\tsize_t result = fwrite(data, 1, size, static_cast<FILE*>(file));\r\n\t\t(void)!result; // unfortunately we can't do proper error handling here\r\n\t}\r\n\r\n#ifndef PUGIXML_NO_STL\r\n\tPUGI_IMPL_FN xml_writer_stream::xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream): narrow_stream(&stream), wide_stream(0)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_writer_stream::xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream): narrow_stream(0), wide_stream(&stream)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xml_writer_stream::write(const void* data, size_t size)\r\n\t{\r\n\t\tif (narrow_stream)\r\n\t\t{\r\n\t\t\tassert(!wide_stream);\r\n\t\t\tnarrow_stream->write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(size));\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tassert(wide_stream);\r\n\t\t\tassert(size % sizeof(wchar_t) == 0);\r\n\r\n\t\t\twide_stream->write(reinterpret_cast<const wchar_t*>(data), static_cast<std::streamsize>(size / sizeof(wchar_t)));\r\n\t\t}\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xml_tree_walker::xml_tree_walker(): _depth(0)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_tree_walker::~xml_tree_walker()\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN int xml_tree_walker::depth() const\r\n\t{\r\n\t\treturn _depth;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_tree_walker::begin(xml_node&)\r\n\t{\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_tree_walker::end(xml_node&)\r\n\t{\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute::xml_attribute(): _attr(0)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN static void unspecified_bool_xml_attribute(xml_attribute***)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute::operator xml_attribute::unspecified_bool_type() const\r\n\t{\r\n\t\treturn _attr ? unspecified_bool_xml_attribute : 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::operator!() const\r\n\t{\r\n\t\treturn !_attr;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::operator==(const xml_attribute& r) const\r\n\t{\r\n\t\treturn (_attr == r._attr);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::operator!=(const xml_attribute& r) const\r\n\t{\r\n\t\treturn (_attr != r._attr);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::operator<(const xml_attribute& r) const\r\n\t{\r\n\t\treturn (_attr < r._attr);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::operator>(const xml_attribute& r) const\r\n\t{\r\n\t\treturn (_attr > r._attr);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::operator<=(const xml_attribute& r) const\r\n\t{\r\n\t\treturn (_attr <= r._attr);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::operator>=(const xml_attribute& r) const\r\n\t{\r\n\t\treturn (_attr >= r._attr);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_attribute::next_attribute() const\r\n\t{\r\n\t\tif (!_attr) return xml_attribute();\r\n\t\treturn xml_attribute(_attr->next_attribute);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_attribute::previous_attribute() const\r\n\t{\r\n\t\tif (!_attr) return xml_attribute();\r\n\t\txml_attribute_struct* prev = _attr->prev_attribute_c;\r\n\t\treturn prev->next_attribute ? xml_attribute(prev) : xml_attribute();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* xml_attribute::as_string(const char_t* def) const\r\n\t{\r\n\t\tif (!_attr) return def;\r\n\t\tconst char_t* value = _attr->value;\r\n\t\treturn value ? value : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN int xml_attribute::as_int(int def) const\r\n\t{\r\n\t\tif (!_attr) return def;\r\n\t\tconst char_t* value = _attr->value;\r\n\t\treturn value ? impl::get_value_int(value) : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN unsigned int xml_attribute::as_uint(unsigned int def) const\r\n\t{\r\n\t\tif (!_attr) return def;\r\n\t\tconst char_t* value = _attr->value;\r\n\t\treturn value ? impl::get_value_uint(value) : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN double xml_attribute::as_double(double def) const\r\n\t{\r\n\t\tif (!_attr) return def;\r\n\t\tconst char_t* value = _attr->value;\r\n\t\treturn value ? impl::get_value_double(value) : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN float xml_attribute::as_float(float def) const\r\n\t{\r\n\t\tif (!_attr) return def;\r\n\t\tconst char_t* value = _attr->value;\r\n\t\treturn value ? impl::get_value_float(value) : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::as_bool(bool def) const\r\n\t{\r\n\t\tif (!_attr) return def;\r\n\t\tconst char_t* value = _attr->value;\r\n\t\treturn value ? impl::get_value_bool(value) : def;\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_LONG_LONG\r\n\tPUGI_IMPL_FN long long xml_attribute::as_llong(long long def) const\r\n\t{\r\n\t\tif (!_attr) return def;\r\n\t\tconst char_t* value = _attr->value;\r\n\t\treturn value ? impl::get_value_llong(value) : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const\r\n\t{\r\n\t\tif (!_attr) return def;\r\n\t\tconst char_t* value = _attr->value;\r\n\t\treturn value ? impl::get_value_ullong(value) : def;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::empty() const\r\n\t{\r\n\t\treturn !_attr;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* xml_attribute::name() const\r\n\t{\r\n\t\tif (!_attr) return PUGIXML_TEXT(\"\");\r\n\t\tconst char_t* name = _attr->name;\r\n\t\treturn name ? name : PUGIXML_TEXT(\"\");\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* xml_attribute::value() const\r\n\t{\r\n\t\tif (!_attr) return PUGIXML_TEXT(\"\");\r\n\t\tconst char_t* value = _attr->value;\r\n\t\treturn value ? value : PUGIXML_TEXT(\"\");\r\n\t}\r\n\r\n\tPUGI_IMPL_FN size_t xml_attribute::hash_value() const\r\n\t{\r\n\t\treturn static_cast<size_t>(reinterpret_cast<uintptr_t>(_attr) / sizeof(xml_attribute_struct));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute_struct* xml_attribute::internal_object() const\r\n\t{\r\n\t\treturn _attr;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute& xml_attribute::operator=(const char_t* rhs)\r\n\t{\r\n\t\tset_value(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute& xml_attribute::operator=(int rhs)\r\n\t{\r\n\t\tset_value(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned int rhs)\r\n\t{\r\n\t\tset_value(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute& xml_attribute::operator=(long rhs)\r\n\t{\r\n\t\tset_value(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned long rhs)\r\n\t{\r\n\t\tset_value(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute& xml_attribute::operator=(double rhs)\r\n\t{\r\n\t\tset_value(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute& xml_attribute::operator=(float rhs)\r\n\t{\r\n\t\tset_value(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute& xml_attribute::operator=(bool rhs)\r\n\t{\r\n\t\tset_value(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_LONG_LONG\r\n\tPUGI_IMPL_FN xml_attribute& xml_attribute::operator=(long long rhs)\r\n\t{\r\n\t\tset_value(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned long long rhs)\r\n\t{\r\n\t\tset_value(rhs);\r\n\t\treturn *this;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_name(const char_t* rhs)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_name(const char_t* rhs, size_t size)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, size);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs, size_t size)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, size);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(int rhs)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(unsigned int rhs)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(long rhs)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(unsigned long rhs)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(double rhs)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(double rhs, int precision)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(float rhs)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(float rhs, int precision)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(bool rhs)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::set_value_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_LONG_LONG\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(long long rhs)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::set_value_integer<unsigned long long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute::set_value(unsigned long long rhs)\r\n\t{\r\n\t\tif (!_attr) return false;\r\n\r\n\t\treturn impl::set_value_integer<unsigned long long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);\r\n\t}\r\n#endif\r\n\r\n#ifdef __BORLANDC__\r\n\tPUGI_IMPL_FN bool operator&&(const xml_attribute& lhs, bool rhs)\r\n\t{\r\n\t\treturn (bool)lhs && rhs;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool operator||(const xml_attribute& lhs, bool rhs)\r\n\t{\r\n\t\treturn (bool)lhs || rhs;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xml_node::xml_node(): _root(0)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node::xml_node(xml_node_struct* p): _root(p)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN static void unspecified_bool_xml_node(xml_node***)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node::operator xml_node::unspecified_bool_type() const\r\n\t{\r\n\t\treturn _root ? unspecified_bool_xml_node : 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::operator!() const\r\n\t{\r\n\t\treturn !_root;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node::iterator xml_node::begin() const\r\n\t{\r\n\t\treturn iterator(_root ? _root->first_child + 0 : 0, _root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node::iterator xml_node::end() const\r\n\t{\r\n\t\treturn iterator(0, _root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node::attribute_iterator xml_node::attributes_begin() const\r\n\t{\r\n\t\treturn attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node::attribute_iterator xml_node::attributes_end() const\r\n\t{\r\n\t\treturn attribute_iterator(0, _root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_object_range<xml_node_iterator> xml_node::children() const\r\n\t{\r\n\t\treturn xml_object_range<xml_node_iterator>(begin(), end());\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_object_range<xml_named_node_iterator> xml_node::children(const char_t* name_) const\r\n\t{\r\n\t\treturn xml_object_range<xml_named_node_iterator>(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_object_range<xml_attribute_iterator> xml_node::attributes() const\r\n\t{\r\n\t\treturn xml_object_range<xml_attribute_iterator>(attributes_begin(), attributes_end());\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::operator==(const xml_node& r) const\r\n\t{\r\n\t\treturn (_root == r._root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::operator!=(const xml_node& r) const\r\n\t{\r\n\t\treturn (_root != r._root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::operator<(const xml_node& r) const\r\n\t{\r\n\t\treturn (_root < r._root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::operator>(const xml_node& r) const\r\n\t{\r\n\t\treturn (_root > r._root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::operator<=(const xml_node& r) const\r\n\t{\r\n\t\treturn (_root <= r._root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::operator>=(const xml_node& r) const\r\n\t{\r\n\t\treturn (_root >= r._root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::empty() const\r\n\t{\r\n\t\treturn !_root;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* xml_node::name() const\r\n\t{\r\n\t\tif (!_root) return PUGIXML_TEXT(\"\");\r\n\t\tconst char_t* name = _root->name;\r\n\t\treturn name ? name : PUGIXML_TEXT(\"\");\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node_type xml_node::type() const\r\n\t{\r\n\t\treturn _root ? PUGI_IMPL_NODETYPE(_root) : node_null;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* xml_node::value() const\r\n\t{\r\n\t\tif (!_root) return PUGIXML_TEXT(\"\");\r\n\t\tconst char_t* value = _root->value;\r\n\t\treturn value ? value : PUGIXML_TEXT(\"\");\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::child(const char_t* name_) const\r\n\t{\r\n\t\tif (!_root) return xml_node();\r\n\r\n\t\tfor (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)\r\n\t\t{\r\n\t\t\tconst char_t* iname = i->name;\r\n\t\t\tif (iname && impl::strequal(name_, iname))\r\n\t\t\t\treturn xml_node(i);\r\n\t\t}\r\n\r\n\t\treturn xml_node();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::attribute(const char_t* name_) const\r\n\t{\r\n\t\tif (!_root) return xml_attribute();\r\n\r\n\t\tfor (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)\r\n\t\t{\r\n\t\t\tconst char_t* iname = i->name;\r\n\t\t\tif (iname && impl::strequal(name_, iname))\r\n\t\t\t\treturn xml_attribute(i);\r\n\t\t}\r\n\r\n\t\treturn xml_attribute();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::next_sibling(const char_t* name_) const\r\n\t{\r\n\t\tif (!_root) return xml_node();\r\n\r\n\t\tfor (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)\r\n\t\t{\r\n\t\t\tconst char_t* iname = i->name;\r\n\t\t\tif (iname && impl::strequal(name_, iname))\r\n\t\t\t\treturn xml_node(i);\r\n\t\t}\r\n\r\n\t\treturn xml_node();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::next_sibling() const\r\n\t{\r\n\t\treturn _root ? xml_node(_root->next_sibling) : xml_node();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::previous_sibling(const char_t* name_) const\r\n\t{\r\n\t\tif (!_root) return xml_node();\r\n\r\n\t\tfor (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c)\r\n\t\t{\r\n\t\t\tconst char_t* iname = i->name;\r\n\t\t\tif (iname && impl::strequal(name_, iname))\r\n\t\t\t\treturn xml_node(i);\r\n\t\t}\r\n\r\n\t\treturn xml_node();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const\r\n\t{\r\n\t\txml_attribute_struct* hint = hint_._attr;\r\n\r\n\t\t// if hint is not an attribute of node, behavior is not defined\r\n\t\tassert(!hint || (_root && impl::is_attribute_of(hint, _root)));\r\n\r\n\t\tif (!_root) return xml_attribute();\r\n\r\n\t\t// optimistically search from hint up until the end\r\n\t\tfor (xml_attribute_struct* i = hint; i; i = i->next_attribute)\r\n\t\t{\r\n\t\t\tconst char_t* iname = i->name;\r\n\t\t\tif (iname && impl::strequal(name_, iname))\r\n\t\t\t{\r\n\t\t\t\t// update hint to maximize efficiency of searching for consecutive attributes\r\n\t\t\t\thint_._attr = i->next_attribute;\r\n\r\n\t\t\t\treturn xml_attribute(i);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// wrap around and search from the first attribute until the hint\r\n\t\t// 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails\r\n\t\tfor (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute)\r\n\t\t{\r\n\t\t\tconst char_t* jname = j->name;\r\n\t\t\tif (jname && impl::strequal(name_, jname))\r\n\t\t\t{\r\n\t\t\t\t// update hint to maximize efficiency of searching for consecutive attributes\r\n\t\t\t\thint_._attr = j->next_attribute;\r\n\r\n\t\t\t\treturn xml_attribute(j);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn xml_attribute();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::previous_sibling() const\r\n\t{\r\n\t\tif (!_root) return xml_node();\r\n\t\txml_node_struct* prev = _root->prev_sibling_c;\r\n\t\treturn prev->next_sibling ? xml_node(prev) : xml_node();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::parent() const\r\n\t{\r\n\t\treturn _root ? xml_node(_root->parent) : xml_node();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::root() const\r\n\t{\r\n\t\treturn _root ? xml_node(&impl::get_document(_root)) : xml_node();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_text xml_node::text() const\r\n\t{\r\n\t\treturn xml_text(_root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* xml_node::child_value() const\r\n\t{\r\n\t\tif (!_root) return PUGIXML_TEXT(\"\");\r\n\r\n\t\t// element nodes can have value if parse_embed_pcdata was used\r\n\t\tif (PUGI_IMPL_NODETYPE(_root) == node_element && _root->value)\r\n\t\t\treturn _root->value;\r\n\r\n\t\tfor (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)\r\n\t\t{\r\n\t\t\tconst char_t* ivalue = i->value;\r\n\t\t\tif (impl::is_text_node(i) && ivalue)\r\n\t\t\t\treturn ivalue;\r\n\t\t}\r\n\r\n\t\treturn PUGIXML_TEXT(\"\");\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* xml_node::child_value(const char_t* name_) const\r\n\t{\r\n\t\treturn child(name_).child_value();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::first_attribute() const\r\n\t{\r\n\t\tif (!_root) return xml_attribute();\r\n\t\treturn xml_attribute(_root->first_attribute);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::last_attribute() const\r\n\t{\r\n\t\tif (!_root) return xml_attribute();\r\n\t\txml_attribute_struct* first = _root->first_attribute;\r\n\t\treturn first ? xml_attribute(first->prev_attribute_c) : xml_attribute();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::first_child() const\r\n\t{\r\n\t\tif (!_root) return xml_node();\r\n\t\treturn xml_node(_root->first_child);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::last_child() const\r\n\t{\r\n\t\tif (!_root) return xml_node();\r\n\t\txml_node_struct* first = _root->first_child;\r\n\t\treturn first ? xml_node(first->prev_sibling_c) : xml_node();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::set_name(const char_t* rhs)\r\n\t{\r\n\t\txml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;\r\n\r\n\t\tif (type_ != node_element && type_ != node_pi && type_ != node_declaration)\r\n\t\t\treturn false;\r\n\r\n\t\treturn impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::set_name(const char_t* rhs, size_t size)\r\n\t{\r\n\t\txml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;\r\n\r\n\t\tif (type_ != node_element && type_ != node_pi && type_ != node_declaration)\r\n\t\t\treturn false;\r\n\r\n\t\treturn impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, size);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs)\r\n\t{\r\n\t\txml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;\r\n\r\n\t\tif (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype)\r\n\t\t\treturn false;\r\n\r\n\t\treturn impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs, size_t size)\r\n\t{\r\n\t\txml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null;\r\n\r\n\t\tif (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype)\r\n\t\t\treturn false;\r\n\r\n\t\treturn impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, size);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::append_attribute(const char_t* name_)\r\n\t{\r\n\t\tif (!impl::allow_insert_attribute(type())) return xml_attribute();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_attribute();\r\n\r\n\t\txml_attribute a(impl::allocate_attribute(alloc));\r\n\t\tif (!a) return xml_attribute();\r\n\r\n\t\timpl::append_attribute(a._attr, _root);\r\n\r\n\t\ta.set_name(name_);\r\n\r\n\t\treturn a;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::prepend_attribute(const char_t* name_)\r\n\t{\r\n\t\tif (!impl::allow_insert_attribute(type())) return xml_attribute();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_attribute();\r\n\r\n\t\txml_attribute a(impl::allocate_attribute(alloc));\r\n\t\tif (!a) return xml_attribute();\r\n\r\n\t\timpl::prepend_attribute(a._attr, _root);\r\n\r\n\t\ta.set_name(name_);\r\n\r\n\t\treturn a;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr)\r\n\t{\r\n\t\tif (!impl::allow_insert_attribute(type())) return xml_attribute();\r\n\t\tif (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_attribute();\r\n\r\n\t\txml_attribute a(impl::allocate_attribute(alloc));\r\n\t\tif (!a) return xml_attribute();\r\n\r\n\t\timpl::insert_attribute_after(a._attr, attr._attr, _root);\r\n\r\n\t\ta.set_name(name_);\r\n\r\n\t\treturn a;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr)\r\n\t{\r\n\t\tif (!impl::allow_insert_attribute(type())) return xml_attribute();\r\n\t\tif (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_attribute();\r\n\r\n\t\txml_attribute a(impl::allocate_attribute(alloc));\r\n\t\tif (!a) return xml_attribute();\r\n\r\n\t\timpl::insert_attribute_before(a._attr, attr._attr, _root);\r\n\r\n\t\ta.set_name(name_);\r\n\r\n\t\treturn a;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::append_copy(const xml_attribute& proto)\r\n\t{\r\n\t\tif (!proto) return xml_attribute();\r\n\t\tif (!impl::allow_insert_attribute(type())) return xml_attribute();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_attribute();\r\n\r\n\t\txml_attribute a(impl::allocate_attribute(alloc));\r\n\t\tif (!a) return xml_attribute();\r\n\r\n\t\timpl::append_attribute(a._attr, _root);\r\n\t\timpl::node_copy_attribute(a._attr, proto._attr);\r\n\r\n\t\treturn a;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto)\r\n\t{\r\n\t\tif (!proto) return xml_attribute();\r\n\t\tif (!impl::allow_insert_attribute(type())) return xml_attribute();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_attribute();\r\n\r\n\t\txml_attribute a(impl::allocate_attribute(alloc));\r\n\t\tif (!a) return xml_attribute();\r\n\r\n\t\timpl::prepend_attribute(a._attr, _root);\r\n\t\timpl::node_copy_attribute(a._attr, proto._attr);\r\n\r\n\t\treturn a;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)\r\n\t{\r\n\t\tif (!proto) return xml_attribute();\r\n\t\tif (!impl::allow_insert_attribute(type())) return xml_attribute();\r\n\t\tif (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_attribute();\r\n\r\n\t\txml_attribute a(impl::allocate_attribute(alloc));\r\n\t\tif (!a) return xml_attribute();\r\n\r\n\t\timpl::insert_attribute_after(a._attr, attr._attr, _root);\r\n\t\timpl::node_copy_attribute(a._attr, proto._attr);\r\n\r\n\t\treturn a;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr)\r\n\t{\r\n\t\tif (!proto) return xml_attribute();\r\n\t\tif (!impl::allow_insert_attribute(type())) return xml_attribute();\r\n\t\tif (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_attribute();\r\n\r\n\t\txml_attribute a(impl::allocate_attribute(alloc));\r\n\t\tif (!a) return xml_attribute();\r\n\r\n\t\timpl::insert_attribute_before(a._attr, attr._attr, _root);\r\n\t\timpl::node_copy_attribute(a._attr, proto._attr);\r\n\r\n\t\treturn a;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::append_child(xml_node_type type_)\r\n\t{\r\n\t\tif (!impl::allow_insert_child(type(), type_)) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\txml_node n(impl::allocate_node(alloc, type_));\r\n\t\tif (!n) return xml_node();\r\n\r\n\t\timpl::append_node(n._root, _root);\r\n\r\n\t\tif (type_ == node_declaration) n.set_name(PUGIXML_TEXT(\"xml\"));\r\n\r\n\t\treturn n;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::prepend_child(xml_node_type type_)\r\n\t{\r\n\t\tif (!impl::allow_insert_child(type(), type_)) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\txml_node n(impl::allocate_node(alloc, type_));\r\n\t\tif (!n) return xml_node();\r\n\r\n\t\timpl::prepend_node(n._root, _root);\r\n\r\n\t\tif (type_ == node_declaration) n.set_name(PUGIXML_TEXT(\"xml\"));\r\n\r\n\t\treturn n;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node)\r\n\t{\r\n\t\tif (!impl::allow_insert_child(type(), type_)) return xml_node();\r\n\t\tif (!node._root || node._root->parent != _root) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\txml_node n(impl::allocate_node(alloc, type_));\r\n\t\tif (!n) return xml_node();\r\n\r\n\t\timpl::insert_node_before(n._root, node._root);\r\n\r\n\t\tif (type_ == node_declaration) n.set_name(PUGIXML_TEXT(\"xml\"));\r\n\r\n\t\treturn n;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node)\r\n\t{\r\n\t\tif (!impl::allow_insert_child(type(), type_)) return xml_node();\r\n\t\tif (!node._root || node._root->parent != _root) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\txml_node n(impl::allocate_node(alloc, type_));\r\n\t\tif (!n) return xml_node();\r\n\r\n\t\timpl::insert_node_after(n._root, node._root);\r\n\r\n\t\tif (type_ == node_declaration) n.set_name(PUGIXML_TEXT(\"xml\"));\r\n\r\n\t\treturn n;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::append_child(const char_t* name_)\r\n\t{\r\n\t\txml_node result = append_child(node_element);\r\n\r\n\t\tresult.set_name(name_);\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::prepend_child(const char_t* name_)\r\n\t{\r\n\t\txml_node result = prepend_child(node_element);\r\n\r\n\t\tresult.set_name(name_);\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node)\r\n\t{\r\n\t\txml_node result = insert_child_after(node_element, node);\r\n\r\n\t\tresult.set_name(name_);\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node)\r\n\t{\r\n\t\txml_node result = insert_child_before(node_element, node);\r\n\r\n\t\tresult.set_name(name_);\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::append_copy(const xml_node& proto)\r\n\t{\r\n\t\txml_node_type type_ = proto.type();\r\n\t\tif (!impl::allow_insert_child(type(), type_)) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\txml_node n(impl::allocate_node(alloc, type_));\r\n\t\tif (!n) return xml_node();\r\n\r\n\t\timpl::append_node(n._root, _root);\r\n\t\timpl::node_copy_tree(n._root, proto._root);\r\n\r\n\t\treturn n;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::prepend_copy(const xml_node& proto)\r\n\t{\r\n\t\txml_node_type type_ = proto.type();\r\n\t\tif (!impl::allow_insert_child(type(), type_)) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\txml_node n(impl::allocate_node(alloc, type_));\r\n\t\tif (!n) return xml_node();\r\n\r\n\t\timpl::prepend_node(n._root, _root);\r\n\t\timpl::node_copy_tree(n._root, proto._root);\r\n\r\n\t\treturn n;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node)\r\n\t{\r\n\t\txml_node_type type_ = proto.type();\r\n\t\tif (!impl::allow_insert_child(type(), type_)) return xml_node();\r\n\t\tif (!node._root || node._root->parent != _root) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\txml_node n(impl::allocate_node(alloc, type_));\r\n\t\tif (!n) return xml_node();\r\n\r\n\t\timpl::insert_node_after(n._root, node._root);\r\n\t\timpl::node_copy_tree(n._root, proto._root);\r\n\r\n\t\treturn n;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node)\r\n\t{\r\n\t\txml_node_type type_ = proto.type();\r\n\t\tif (!impl::allow_insert_child(type(), type_)) return xml_node();\r\n\t\tif (!node._root || node._root->parent != _root) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\txml_node n(impl::allocate_node(alloc, type_));\r\n\t\tif (!n) return xml_node();\r\n\r\n\t\timpl::insert_node_before(n._root, node._root);\r\n\t\timpl::node_copy_tree(n._root, proto._root);\r\n\r\n\t\treturn n;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::append_move(const xml_node& moved)\r\n\t{\r\n\t\tif (!impl::allow_move(*this, moved)) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\t// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers\r\n\t\timpl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;\r\n\r\n\t\timpl::remove_node(moved._root);\r\n\t\timpl::append_node(moved._root, _root);\r\n\r\n\t\treturn moved;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::prepend_move(const xml_node& moved)\r\n\t{\r\n\t\tif (!impl::allow_move(*this, moved)) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\t// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers\r\n\t\timpl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;\r\n\r\n\t\timpl::remove_node(moved._root);\r\n\t\timpl::prepend_node(moved._root, _root);\r\n\r\n\t\treturn moved;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node)\r\n\t{\r\n\t\tif (!impl::allow_move(*this, moved)) return xml_node();\r\n\t\tif (!node._root || node._root->parent != _root) return xml_node();\r\n\t\tif (moved._root == node._root) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\t// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers\r\n\t\timpl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;\r\n\r\n\t\timpl::remove_node(moved._root);\r\n\t\timpl::insert_node_after(moved._root, node._root);\r\n\r\n\t\treturn moved;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node)\r\n\t{\r\n\t\tif (!impl::allow_move(*this, moved)) return xml_node();\r\n\t\tif (!node._root || node._root->parent != _root) return xml_node();\r\n\t\tif (moved._root == node._root) return xml_node();\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return xml_node();\r\n\r\n\t\t// disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers\r\n\t\timpl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;\r\n\r\n\t\timpl::remove_node(moved._root);\r\n\t\timpl::insert_node_before(moved._root, node._root);\r\n\r\n\t\treturn moved;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::remove_attribute(const char_t* name_)\r\n\t{\r\n\t\treturn remove_attribute(attribute(name_));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::remove_attribute(const xml_attribute& a)\r\n\t{\r\n\t\tif (!_root || !a._attr) return false;\r\n\t\tif (!impl::is_attribute_of(a._attr, _root)) return false;\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return false;\r\n\r\n\t\timpl::remove_attribute(a._attr, _root);\r\n\t\timpl::destroy_attribute(a._attr, alloc);\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::remove_attributes()\r\n\t{\r\n\t\tif (!_root) return false;\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return false;\r\n\r\n\t\tfor (xml_attribute_struct* attr = _root->first_attribute; attr; )\r\n\t\t{\r\n\t\t\txml_attribute_struct* next = attr->next_attribute;\r\n\r\n\t\t\timpl::destroy_attribute(attr, alloc);\r\n\r\n\t\t\tattr = next;\r\n\t\t}\r\n\r\n\t\t_root->first_attribute = 0;\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::remove_child(const char_t* name_)\r\n\t{\r\n\t\treturn remove_child(child(name_));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::remove_child(const xml_node& n)\r\n\t{\r\n\t\tif (!_root || !n._root || n._root->parent != _root) return false;\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return false;\r\n\r\n\t\timpl::remove_node(n._root);\r\n\t\timpl::destroy_node(n._root, alloc);\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::remove_children()\r\n\t{\r\n\t\tif (!_root) return false;\r\n\r\n\t\timpl::xml_allocator& alloc = impl::get_allocator(_root);\r\n\t\tif (!alloc.reserve()) return false;\r\n\r\n\t\tfor (xml_node_struct* cur = _root->first_child; cur; )\r\n\t\t{\r\n\t\t\txml_node_struct* next = cur->next_sibling;\r\n\r\n\t\t\timpl::destroy_node(cur, alloc);\r\n\r\n\t\t\tcur = next;\r\n\t\t}\r\n\r\n\t\t_root->first_child = 0;\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)\r\n\t{\r\n\t\t// append_buffer is only valid for elements/documents\r\n\t\tif (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root);\r\n\r\n\t\t// append buffer can not merge PCDATA into existing PCDATA nodes\r\n\t\tif ((options & parse_merge_pcdata) != 0 && last_child().type() == node_pcdata) return impl::make_parse_result(status_append_invalid_root);\r\n\r\n\t\t// get document node\r\n\t\timpl::xml_document_struct* doc = &impl::get_document(_root);\r\n\r\n\t\t// disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense\r\n\t\tdoc->header |= impl::xml_memory_page_contents_shared_mask;\r\n\r\n\t\t// get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later)\r\n\t\timpl::xml_memory_page* page = 0;\r\n\t\timpl::xml_extra_buffer* extra = static_cast<impl::xml_extra_buffer*>(doc->allocate_memory(sizeof(impl::xml_extra_buffer) + sizeof(void*), page));\r\n\t\t(void)page;\r\n\r\n\t\tif (!extra) return impl::make_parse_result(status_out_of_memory);\r\n\r\n\t#ifdef PUGIXML_COMPACT\r\n\t\t// align the memory block to a pointer boundary; this is required for compact mode where memory allocations are only 4b aligned\r\n\t\t// note that this requires up to sizeof(void*)-1 additional memory, which the allocation above takes into account\r\n\t\textra = reinterpret_cast<impl::xml_extra_buffer*>((reinterpret_cast<uintptr_t>(extra) + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1));\r\n\t#endif\r\n\r\n\t\t// add extra buffer to the list\r\n\t\textra->buffer = 0;\r\n\t\textra->next = doc->extra_buffers;\r\n\t\tdoc->extra_buffers = extra;\r\n\r\n\t\t// name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level\r\n\t\timpl::name_null_sentry sentry(_root);\r\n\r\n\t\treturn impl::load_buffer_impl(doc, _root, const_cast<void*>(contents), size, options, encoding, false, false, &extra->buffer);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const\r\n\t{\r\n\t\tif (!_root) return xml_node();\r\n\r\n\t\tfor (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)\r\n\t\t{\r\n\t\t\tconst char_t* iname = i->name;\r\n\t\t\tif (iname && impl::strequal(name_, iname))\r\n\t\t\t{\r\n\t\t\t\tfor (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst char_t* aname = a->name;\r\n\t\t\t\t\tif (aname && impl::strequal(attr_name, aname))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst char_t* avalue = a->value;\r\n\t\t\t\t\t\tif (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT(\"\")))\r\n\t\t\t\t\t\t\treturn xml_node(i);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn xml_node();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const\r\n\t{\r\n\t\tif (!_root) return xml_node();\r\n\r\n\t\tfor (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)\r\n\t\t\tfor (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)\r\n\t\t\t{\r\n\t\t\t\tconst char_t* aname = a->name;\r\n\t\t\t\tif (aname && impl::strequal(attr_name, aname))\r\n\t\t\t\t{\r\n\t\t\t\t\tconst char_t* avalue = a->value;\r\n\t\t\t\t\tif (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT(\"\")))\r\n\t\t\t\t\t\treturn xml_node(i);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\treturn xml_node();\r\n\t}\r\n\r\n#ifndef PUGIXML_NO_STL\r\n\tPUGI_IMPL_FN string_t xml_node::path(char_t delimiter) const\r\n\t{\r\n\t\tif (!_root) return string_t();\r\n\r\n\t\tsize_t offset = 0;\r\n\r\n\t\tfor (xml_node_struct* i = _root; i; i = i->parent)\r\n\t\t{\r\n\t\t\tconst char_t* iname = i->name;\r\n\t\t\toffset += (i != _root);\r\n\t\t\toffset += iname ? impl::strlength(iname) : 0;\r\n\t\t}\r\n\r\n\t\tstring_t result;\r\n\t\tresult.resize(offset);\r\n\r\n\t\tfor (xml_node_struct* j = _root; j; j = j->parent)\r\n\t\t{\r\n\t\t\tif (j != _root)\r\n\t\t\t\tresult[--offset] = delimiter;\r\n\r\n\t\t\tconst char_t* jname = j->name;\r\n\t\t\tif (jname)\r\n\t\t\t{\r\n\t\t\t\tsize_t length = impl::strlength(jname);\r\n\r\n\t\t\t\toffset -= length;\r\n\t\t\t\tmemcpy(&result[offset], jname, length * sizeof(char_t));\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tassert(offset == 0);\r\n\r\n\t\treturn result;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const\r\n\t{\r\n\t\txml_node context = path_[0] == delimiter ? root() : *this;\r\n\r\n\t\tif (!context._root) return xml_node();\r\n\r\n\t\tconst char_t* path_segment = path_;\r\n\r\n\t\twhile (*path_segment == delimiter) ++path_segment;\r\n\r\n\t\tconst char_t* path_segment_end = path_segment;\r\n\r\n\t\twhile (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end;\r\n\r\n\t\tif (path_segment == path_segment_end) return context;\r\n\r\n\t\tconst char_t* next_segment = path_segment_end;\r\n\r\n\t\twhile (*next_segment == delimiter) ++next_segment;\r\n\r\n\t\tif (*path_segment == '.' && path_segment + 1 == path_segment_end)\r\n\t\t\treturn context.first_element_by_path(next_segment, delimiter);\r\n\t\telse if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end)\r\n\t\t\treturn context.parent().first_element_by_path(next_segment, delimiter);\r\n\t\telse\r\n\t\t{\r\n\t\t\tfor (xml_node_struct* j = context._root->first_child; j; j = j->next_sibling)\r\n\t\t\t{\r\n\t\t\t\tconst char_t* jname = j->name;\r\n\t\t\t\tif (jname && impl::strequalrange(jname, path_segment, static_cast<size_t>(path_segment_end - path_segment)))\r\n\t\t\t\t{\r\n\t\t\t\t\txml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter);\r\n\r\n\t\t\t\t\tif (subsearch) return subsearch;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\treturn xml_node();\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node::traverse(xml_tree_walker& walker)\r\n\t{\r\n\t\twalker._depth = -1;\r\n\r\n\t\txml_node arg_begin(_root);\r\n\t\tif (!walker.begin(arg_begin)) return false;\r\n\r\n\t\txml_node_struct* cur = _root ? _root->first_child + 0 : 0;\r\n\r\n\t\tif (cur)\r\n\t\t{\r\n\t\t\t++walker._depth;\r\n\r\n\t\t\tdo\r\n\t\t\t{\r\n\t\t\t\txml_node arg_for_each(cur);\r\n\t\t\t\tif (!walker.for_each(arg_for_each))\r\n\t\t\t\t\treturn false;\r\n\r\n\t\t\t\tif (cur->first_child)\r\n\t\t\t\t{\r\n\t\t\t\t\t++walker._depth;\r\n\t\t\t\t\tcur = cur->first_child;\r\n\t\t\t\t}\r\n\t\t\t\telse if (cur->next_sibling)\r\n\t\t\t\t\tcur = cur->next_sibling;\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\twhile (!cur->next_sibling && cur != _root && cur->parent)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t--walker._depth;\r\n\t\t\t\t\t\tcur = cur->parent;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (cur != _root)\r\n\t\t\t\t\t\tcur = cur->next_sibling;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\twhile (cur && cur != _root);\r\n\t\t}\r\n\r\n\t\tassert(walker._depth == -1);\r\n\r\n\t\txml_node arg_end(_root);\r\n\t\treturn walker.end(arg_end);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN size_t xml_node::hash_value() const\r\n\t{\r\n\t\treturn static_cast<size_t>(reinterpret_cast<uintptr_t>(_root) / sizeof(xml_node_struct));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node_struct* xml_node::internal_object() const\r\n\t{\r\n\t\treturn _root;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const\r\n\t{\r\n\t\tif (!_root) return;\r\n\r\n\t\timpl::xml_buffered_writer buffered_writer(writer, encoding);\r\n\r\n\t\timpl::node_output(buffered_writer, _root, indent, flags, depth);\r\n\r\n\t\tbuffered_writer.flush();\r\n\t}\r\n\r\n#ifndef PUGIXML_NO_STL\r\n\tPUGI_IMPL_FN void xml_node::print(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const\r\n\t{\r\n\t\txml_writer_stream writer(stream);\r\n\r\n\t\tprint(writer, indent, flags, encoding, depth);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xml_node::print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const\r\n\t{\r\n\t\txml_writer_stream writer(stream);\r\n\r\n\t\tprint(writer, indent, flags, encoding_wchar, depth);\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN ptrdiff_t xml_node::offset_debug() const\r\n\t{\r\n\t\tif (!_root) return -1;\r\n\r\n\t\timpl::xml_document_struct& doc = impl::get_document(_root);\r\n\r\n\t\t// we can determine the offset reliably only if there is exactly once parse buffer\r\n\t\tif (!doc.buffer || doc.extra_buffers) return -1;\r\n\r\n\t\tswitch (type())\r\n\t\t{\r\n\t\tcase node_document:\r\n\t\t\treturn 0;\r\n\r\n\t\tcase node_element:\r\n\t\tcase node_declaration:\r\n\t\tcase node_pi:\r\n\t\t\treturn _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1;\r\n\r\n\t\tcase node_pcdata:\r\n\t\tcase node_cdata:\r\n\t\tcase node_comment:\r\n\t\tcase node_doctype:\r\n\t\t\treturn _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1;\r\n\r\n\t\tdefault:\r\n\t\t\tassert(false && \"Invalid node type\"); // unreachable\r\n\t\t\treturn -1;\r\n\t\t}\r\n\t}\r\n\r\n#ifdef __BORLANDC__\r\n\tPUGI_IMPL_FN bool operator&&(const xml_node& lhs, bool rhs)\r\n\t{\r\n\t\treturn (bool)lhs && rhs;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool operator||(const xml_node& lhs, bool rhs)\r\n\t{\r\n\t\treturn (bool)lhs || rhs;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xml_text::xml_text(xml_node_struct* root): _root(root)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node_struct* xml_text::_data() const\r\n\t{\r\n\t\tif (!_root || impl::is_text_node(_root)) return _root;\r\n\r\n\t\t// element nodes can have value if parse_embed_pcdata was used\r\n\t\tif (PUGI_IMPL_NODETYPE(_root) == node_element && _root->value)\r\n\t\t\treturn _root;\r\n\r\n\t\tfor (xml_node_struct* node = _root->first_child; node; node = node->next_sibling)\r\n\t\t\tif (impl::is_text_node(node))\r\n\t\t\t\treturn node;\r\n\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node_struct* xml_text::_data_new()\r\n\t{\r\n\t\txml_node_struct* d = _data();\r\n\t\tif (d) return d;\r\n\r\n\t\treturn xml_node(_root).append_child(node_pcdata).internal_object();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_text::xml_text(): _root(0)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN static void unspecified_bool_xml_text(xml_text***)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_text::operator xml_text::unspecified_bool_type() const\r\n\t{\r\n\t\treturn _data() ? unspecified_bool_xml_text : 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::operator!() const\r\n\t{\r\n\t\treturn !_data();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::empty() const\r\n\t{\r\n\t\treturn _data() == 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* xml_text::get() const\r\n\t{\r\n\t\txml_node_struct* d = _data();\r\n\t\tif (!d) return PUGIXML_TEXT(\"\");\r\n\t\tconst char_t* value = d->value;\r\n\t\treturn value ? value : PUGIXML_TEXT(\"\");\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* xml_text::as_string(const char_t* def) const\r\n\t{\r\n\t\txml_node_struct* d = _data();\r\n\t\tif (!d) return def;\r\n\t\tconst char_t* value = d->value;\r\n\t\treturn value ? value : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN int xml_text::as_int(int def) const\r\n\t{\r\n\t\txml_node_struct* d = _data();\r\n\t\tif (!d) return def;\r\n\t\tconst char_t* value = d->value;\r\n\t\treturn value ? impl::get_value_int(value) : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN unsigned int xml_text::as_uint(unsigned int def) const\r\n\t{\r\n\t\txml_node_struct* d = _data();\r\n\t\tif (!d) return def;\r\n\t\tconst char_t* value = d->value;\r\n\t\treturn value ? impl::get_value_uint(value) : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN double xml_text::as_double(double def) const\r\n\t{\r\n\t\txml_node_struct* d = _data();\r\n\t\tif (!d) return def;\r\n\t\tconst char_t* value = d->value;\r\n\t\treturn value ? impl::get_value_double(value) : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN float xml_text::as_float(float def) const\r\n\t{\r\n\t\txml_node_struct* d = _data();\r\n\t\tif (!d) return def;\r\n\t\tconst char_t* value = d->value;\r\n\t\treturn value ? impl::get_value_float(value) : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::as_bool(bool def) const\r\n\t{\r\n\t\txml_node_struct* d = _data();\r\n\t\tif (!d) return def;\r\n\t\tconst char_t* value = d->value;\r\n\t\treturn value ? impl::get_value_bool(value) : def;\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_LONG_LONG\r\n\tPUGI_IMPL_FN long long xml_text::as_llong(long long def) const\r\n\t{\r\n\t\txml_node_struct* d = _data();\r\n\t\tif (!d) return def;\r\n\t\tconst char_t* value = d->value;\r\n\t\treturn value ? impl::get_value_llong(value) : def;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN unsigned long long xml_text::as_ullong(unsigned long long def) const\r\n\t{\r\n\t\txml_node_struct* d = _data();\r\n\t\tif (!d) return def;\r\n\t\tconst char_t* value = d->value;\r\n\t\treturn value ? impl::get_value_ullong(value) : def;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(const char_t* rhs)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(const char_t* rhs, size_t size)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, size) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(int rhs)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(unsigned int rhs)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(long rhs)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(unsigned long rhs)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(float rhs)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(float rhs, int precision)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(double rhs)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(double rhs, int precision)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(bool rhs)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_LONG_LONG\r\n\tPUGI_IMPL_FN bool xml_text::set(long long rhs)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::set_value_integer<unsigned long long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_text::set(unsigned long long rhs)\r\n\t{\r\n\t\txml_node_struct* dn = _data_new();\r\n\r\n\t\treturn dn ? impl::set_value_integer<unsigned long long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xml_text& xml_text::operator=(const char_t* rhs)\r\n\t{\r\n\t\tset(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_text& xml_text::operator=(int rhs)\r\n\t{\r\n\t\tset(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_text& xml_text::operator=(unsigned int rhs)\r\n\t{\r\n\t\tset(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_text& xml_text::operator=(long rhs)\r\n\t{\r\n\t\tset(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_text& xml_text::operator=(unsigned long rhs)\r\n\t{\r\n\t\tset(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_text& xml_text::operator=(double rhs)\r\n\t{\r\n\t\tset(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_text& xml_text::operator=(float rhs)\r\n\t{\r\n\t\tset(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_text& xml_text::operator=(bool rhs)\r\n\t{\r\n\t\tset(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_LONG_LONG\r\n\tPUGI_IMPL_FN xml_text& xml_text::operator=(long long rhs)\r\n\t{\r\n\t\tset(rhs);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_text& xml_text::operator=(unsigned long long rhs)\r\n\t{\r\n\t\tset(rhs);\r\n\t\treturn *this;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xml_node xml_text::data() const\r\n\t{\r\n\t\treturn xml_node(_data());\r\n\t}\r\n\r\n#ifdef __BORLANDC__\r\n\tPUGI_IMPL_FN bool operator&&(const xml_text& lhs, bool rhs)\r\n\t{\r\n\t\treturn (bool)lhs && rhs;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool operator||(const xml_text& lhs, bool rhs)\r\n\t{\r\n\t\treturn (bool)lhs || rhs;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xml_node_iterator::xml_node_iterator()\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent())\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const\r\n\t{\r\n\t\treturn _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const\r\n\t{\r\n\t\treturn _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node& xml_node_iterator::operator*() const\r\n\t{\r\n\t\tassert(_wrap._root);\r\n\t\treturn _wrap;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node* xml_node_iterator::operator->() const\r\n\t{\r\n\t\tassert(_wrap._root);\r\n\t\treturn const_cast<xml_node*>(&_wrap); // BCC5 workaround\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node_iterator& xml_node_iterator::operator++()\r\n\t{\r\n\t\tassert(_wrap._root);\r\n\t\t_wrap._root = _wrap._root->next_sibling;\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node_iterator xml_node_iterator::operator++(int)\r\n\t{\r\n\t\txml_node_iterator temp = *this;\r\n\t\t++*this;\r\n\t\treturn temp;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node_iterator& xml_node_iterator::operator--()\r\n\t{\r\n\t\t_wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child();\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node_iterator xml_node_iterator::operator--(int)\r\n\t{\r\n\t\txml_node_iterator temp = *this;\r\n\t\t--*this;\r\n\t\treturn temp;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator()\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const\r\n\t{\r\n\t\treturn _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const\r\n\t{\r\n\t\treturn _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute& xml_attribute_iterator::operator*() const\r\n\t{\r\n\t\tassert(_wrap._attr);\r\n\t\treturn _wrap;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute* xml_attribute_iterator::operator->() const\r\n\t{\r\n\t\tassert(_wrap._attr);\r\n\t\treturn const_cast<xml_attribute*>(&_wrap); // BCC5 workaround\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute_iterator& xml_attribute_iterator::operator++()\r\n\t{\r\n\t\tassert(_wrap._attr);\r\n\t\t_wrap._attr = _wrap._attr->next_attribute;\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute_iterator xml_attribute_iterator::operator++(int)\r\n\t{\r\n\t\txml_attribute_iterator temp = *this;\r\n\t\t++*this;\r\n\t\treturn temp;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute_iterator& xml_attribute_iterator::operator--()\r\n\t{\r\n\t\t_wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute();\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute_iterator xml_attribute_iterator::operator--(int)\r\n\t{\r\n\t\txml_attribute_iterator temp = *this;\r\n\t\t--*this;\r\n\t\treturn temp;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(): _name(0)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const\r\n\t{\r\n\t\treturn _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const\r\n\t{\r\n\t\treturn _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node& xml_named_node_iterator::operator*() const\r\n\t{\r\n\t\tassert(_wrap._root);\r\n\t\treturn _wrap;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node* xml_named_node_iterator::operator->() const\r\n\t{\r\n\t\tassert(_wrap._root);\r\n\t\treturn const_cast<xml_node*>(&_wrap); // BCC5 workaround\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_named_node_iterator& xml_named_node_iterator::operator++()\r\n\t{\r\n\t\tassert(_wrap._root);\r\n\t\t_wrap = _wrap.next_sibling(_name);\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_named_node_iterator xml_named_node_iterator::operator++(int)\r\n\t{\r\n\t\txml_named_node_iterator temp = *this;\r\n\t\t++*this;\r\n\t\treturn temp;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_named_node_iterator& xml_named_node_iterator::operator--()\r\n\t{\r\n\t\tif (_wrap._root)\r\n\t\t\t_wrap = _wrap.previous_sibling(_name);\r\n\t\telse\r\n\t\t{\r\n\t\t\t_wrap = _parent.last_child();\r\n\r\n\t\t\tif (!impl::strequal(_wrap.name(), _name))\r\n\t\t\t\t_wrap = _wrap.previous_sibling(_name);\r\n\t\t}\r\n\r\n\t\treturn *this;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_named_node_iterator xml_named_node_iterator::operator--(int)\r\n\t{\r\n\t\txml_named_node_iterator temp = *this;\r\n\t\t--*this;\r\n\t\treturn temp;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result::operator bool() const\r\n\t{\r\n\t\treturn status == status_ok;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char* xml_parse_result::description() const\r\n\t{\r\n\t\tswitch (status)\r\n\t\t{\r\n\t\tcase status_ok: return \"No error\";\r\n\r\n\t\tcase status_file_not_found: return \"File was not found\";\r\n\t\tcase status_io_error: return \"Error reading from file/stream\";\r\n\t\tcase status_out_of_memory: return \"Could not allocate memory\";\r\n\t\tcase status_internal_error: return \"Internal error occurred\";\r\n\r\n\t\tcase status_unrecognized_tag: return \"Could not determine tag type\";\r\n\r\n\t\tcase status_bad_pi: return \"Error parsing document declaration/processing instruction\";\r\n\t\tcase status_bad_comment: return \"Error parsing comment\";\r\n\t\tcase status_bad_cdata: return \"Error parsing CDATA section\";\r\n\t\tcase status_bad_doctype: return \"Error parsing document type declaration\";\r\n\t\tcase status_bad_pcdata: return \"Error parsing PCDATA section\";\r\n\t\tcase status_bad_start_element: return \"Error parsing start element tag\";\r\n\t\tcase status_bad_attribute: return \"Error parsing element attribute\";\r\n\t\tcase status_bad_end_element: return \"Error parsing end element tag\";\r\n\t\tcase status_end_element_mismatch: return \"Start-end tags mismatch\";\r\n\r\n\t\tcase status_append_invalid_root: return \"Unable to append nodes: root is not an element or document\";\r\n\r\n\t\tcase status_no_document_element: return \"No document element found\";\r\n\r\n\t\tdefault: return \"Unknown error\";\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_document::xml_document(): _buffer(0)\r\n\t{\r\n\t\t_create();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_document::~xml_document()\r\n\t{\r\n\t\t_destroy();\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_MOVE\r\n\tPUGI_IMPL_FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(0)\r\n\t{\r\n\t\t_create();\r\n\t\t_move(rhs);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_document& xml_document::operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT\r\n\t{\r\n\t\tif (this == &rhs) return *this;\r\n\r\n\t\t_destroy();\r\n\t\t_create();\r\n\t\t_move(rhs);\r\n\r\n\t\treturn *this;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN void xml_document::reset()\r\n\t{\r\n\t\t_destroy();\r\n\t\t_create();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xml_document::reset(const xml_document& proto)\r\n\t{\r\n\t\treset();\r\n\r\n\t\timpl::node_copy_tree(_root, proto._root);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xml_document::_create()\r\n\t{\r\n\t\tassert(!_root);\r\n\r\n\t#ifdef PUGIXML_COMPACT\r\n\t\t// space for page marker for the first page (uint32_t), rounded up to pointer size; assumes pointers are at least 32-bit\r\n\t\tconst size_t page_offset = sizeof(void*);\r\n\t#else\r\n\t\tconst size_t page_offset = 0;\r\n\t#endif\r\n\r\n\t\t// initialize sentinel page\r\n\t\tPUGI_IMPL_STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory));\r\n\r\n\t\t// prepare page structure\r\n\t\timpl::xml_memory_page* page = impl::xml_memory_page::construct(_memory);\r\n\t\tassert(page);\r\n\r\n\t\tpage->busy_size = impl::xml_memory_page_size;\r\n\r\n\t\t// setup first page marker\r\n\t#ifdef PUGIXML_COMPACT\r\n\t\t// round-trip through void* to avoid 'cast increases required alignment of target type' warning\r\n\t\tpage->compact_page_marker = reinterpret_cast<uint32_t*>(static_cast<void*>(reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page)));\r\n\t\t*page->compact_page_marker = sizeof(impl::xml_memory_page);\r\n\t#endif\r\n\r\n\t\t// allocate new root\r\n\t\t_root = new (reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page);\r\n\t\t_root->prev_sibling_c = _root;\r\n\r\n\t\t// setup sentinel page\r\n\t\tpage->allocator = static_cast<impl::xml_document_struct*>(_root);\r\n\r\n\t\t// setup hash table pointer in allocator\r\n\t#ifdef PUGIXML_COMPACT\r\n\t\tpage->allocator->_hash = &static_cast<impl::xml_document_struct*>(_root)->hash;\r\n\t#endif\r\n\r\n\t\t// verify the document allocation\r\n\t\tassert(reinterpret_cast<char*>(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xml_document::_destroy()\r\n\t{\r\n\t\tassert(_root);\r\n\r\n\t\t// destroy static storage\r\n\t\tif (_buffer)\r\n\t\t{\r\n\t\t\timpl::xml_memory::deallocate(_buffer);\r\n\t\t\t_buffer = 0;\r\n\t\t}\r\n\r\n\t\t// destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator)\r\n\t\tfor (impl::xml_extra_buffer* extra = static_cast<impl::xml_document_struct*>(_root)->extra_buffers; extra; extra = extra->next)\r\n\t\t{\r\n\t\t\tif (extra->buffer) impl::xml_memory::deallocate(extra->buffer);\r\n\t\t}\r\n\r\n\t\t// destroy dynamic storage, leave sentinel page (it's in static memory)\r\n\t\timpl::xml_memory_page* root_page = PUGI_IMPL_GETPAGE(_root);\r\n\t\tassert(root_page && !root_page->prev);\r\n\t\tassert(reinterpret_cast<char*>(root_page) >= _memory && reinterpret_cast<char*>(root_page) < _memory + sizeof(_memory));\r\n\r\n\t\tfor (impl::xml_memory_page* page = root_page->next; page; )\r\n\t\t{\r\n\t\t\timpl::xml_memory_page* next = page->next;\r\n\r\n\t\t\timpl::xml_allocator::deallocate_page(page);\r\n\r\n\t\t\tpage = next;\r\n\t\t}\r\n\r\n\t#ifdef PUGIXML_COMPACT\r\n\t\t// destroy hash table\r\n\t\tstatic_cast<impl::xml_document_struct*>(_root)->hash.clear();\r\n\t#endif\r\n\r\n\t\t_root = 0;\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_MOVE\r\n\tPUGI_IMPL_FN void xml_document::_move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT\r\n\t{\r\n\t\timpl::xml_document_struct* doc = static_cast<impl::xml_document_struct*>(_root);\r\n\t\timpl::xml_document_struct* other = static_cast<impl::xml_document_struct*>(rhs._root);\r\n\r\n\t\t// save first child pointer for later; this needs hash access\r\n\t\txml_node_struct* other_first_child = other->first_child;\r\n\r\n\t#ifdef PUGIXML_COMPACT\r\n\t\t// reserve space for the hash table up front; this is the only operation that can fail\r\n\t\t// if it does, we have no choice but to throw (if we have exceptions)\r\n\t\tif (other_first_child)\r\n\t\t{\r\n\t\t\tsize_t other_children = 0;\r\n\t\t\tfor (xml_node_struct* node = other_first_child; node; node = node->next_sibling)\r\n\t\t\t\tother_children++;\r\n\r\n\t\t\t// in compact mode, each pointer assignment could result in a hash table request\r\n\t\t\t// during move, we have to relocate document first_child and parents of all children\r\n\t\t\t// normally there's just one child and its parent has a pointerless encoding but\r\n\t\t\t// we assume the worst here\r\n\t\t\tif (!other->_hash->reserve(other_children + 1))\r\n\t\t\t{\r\n\t\t\t#ifdef PUGIXML_NO_EXCEPTIONS\r\n\t\t\t\treturn;\r\n\t\t\t#else\r\n\t\t\t\tthrow std::bad_alloc();\r\n\t\t\t#endif\r\n\t\t\t}\r\n\t\t}\r\n\t#endif\r\n\r\n\t\t// move allocation state\r\n\t\t// note that other->_root may point to the embedded document page, in which case we should keep original (empty) state\r\n\t\tif (other->_root != PUGI_IMPL_GETPAGE(other))\r\n\t\t{\r\n\t\t\tdoc->_root = other->_root;\r\n\t\t\tdoc->_busy_size = other->_busy_size;\r\n\t\t}\r\n\r\n\t\t// move buffer state\r\n\t\tdoc->buffer = other->buffer;\r\n\t\tdoc->extra_buffers = other->extra_buffers;\r\n\t\t_buffer = rhs._buffer;\r\n\r\n\t#ifdef PUGIXML_COMPACT\r\n\t\t// move compact hash; note that the hash table can have pointers to other but they will be \"inactive\", similarly to nodes removed with remove_child\r\n\t\tdoc->hash = other->hash;\r\n\t\tdoc->_hash = &doc->hash;\r\n\r\n\t\t// make sure we don't access other hash up until the end when we reinitialize other document\r\n\t\tother->_hash = 0;\r\n\t#endif\r\n\r\n\t\t// move page structure\r\n\t\timpl::xml_memory_page* doc_page = PUGI_IMPL_GETPAGE(doc);\r\n\t\tassert(doc_page && !doc_page->prev && !doc_page->next);\r\n\r\n\t\timpl::xml_memory_page* other_page = PUGI_IMPL_GETPAGE(other);\r\n\t\tassert(other_page && !other_page->prev);\r\n\r\n\t\t// relink pages since root page is embedded into xml_document\r\n\t\tif (impl::xml_memory_page* page = other_page->next)\r\n\t\t{\r\n\t\t\tassert(page->prev == other_page);\r\n\r\n\t\t\tpage->prev = doc_page;\r\n\r\n\t\t\tdoc_page->next = page;\r\n\t\t\tother_page->next = 0;\r\n\t\t}\r\n\r\n\t\t// make sure pages point to the correct document state\r\n\t\tfor (impl::xml_memory_page* page = doc_page->next; page; page = page->next)\r\n\t\t{\r\n\t\t\tassert(page->allocator == other);\r\n\r\n\t\t\tpage->allocator = doc;\r\n\r\n\t\t#ifdef PUGIXML_COMPACT\r\n\t\t\t// this automatically migrates most children between documents and prevents ->parent assignment from allocating\r\n\t\t\tif (page->compact_shared_parent == other)\r\n\t\t\t\tpage->compact_shared_parent = doc;\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\t// move tree structure\r\n\t\tassert(!doc->first_child);\r\n\r\n\t\tdoc->first_child = other_first_child;\r\n\r\n\t\tfor (xml_node_struct* node = other_first_child; node; node = node->next_sibling)\r\n\t\t{\r\n\t\t#ifdef PUGIXML_COMPACT\r\n\t\t\t// most children will have migrated when we reassigned compact_shared_parent\r\n\t\t\tassert(node->parent == other || node->parent == doc);\r\n\r\n\t\t\tnode->parent = doc;\r\n\t\t#else\r\n\t\t\tassert(node->parent == other);\r\n\t\t\tnode->parent = doc;\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\t// reset other document\r\n\t\tnew (other) impl::xml_document_struct(PUGI_IMPL_GETPAGE(other));\r\n\t\trhs._buffer = 0;\r\n\t}\r\n#endif\r\n\r\n#ifndef PUGIXML_NO_STL\r\n\tPUGI_IMPL_FN xml_parse_result xml_document::load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options, xml_encoding encoding)\r\n\t{\r\n\t\treset();\r\n\r\n\t\treturn impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding, &_buffer);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result xml_document::load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options)\r\n\t{\r\n\t\treset();\r\n\r\n\t\treturn impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding_wchar, &_buffer);\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options)\r\n\t{\r\n\t\t// Force native encoding (skip autodetection)\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\txml_encoding encoding = encoding_wchar;\r\n\t#else\r\n\t\txml_encoding encoding = encoding_utf8;\r\n\t#endif\r\n\r\n\t\treturn load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options)\r\n\t{\r\n\t\treturn load_string(contents, options);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding)\r\n\t{\r\n\t\treset();\r\n\r\n\t\tusing impl::auto_deleter; // MSVC7 workaround\r\n\t\tauto_deleter<FILE> file(impl::open_file(path_, \"rb\"), impl::close_file);\r\n\r\n\t\treturn impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding)\r\n\t{\r\n\t\treset();\r\n\r\n\t\tusing impl::auto_deleter; // MSVC7 workaround\r\n\t\tauto_deleter<FILE> file(impl::open_file_wide(path_, L\"rb\"), impl::close_file);\r\n\r\n\t\treturn impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)\r\n\t{\r\n\t\treset();\r\n\r\n\t\treturn impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, const_cast<void*>(contents), size, options, encoding, false, false, &_buffer);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding)\r\n\t{\r\n\t\treset();\r\n\r\n\t\treturn impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, false, &_buffer);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding)\r\n\t{\r\n\t\treset();\r\n\r\n\t\treturn impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, true, &_buffer);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const\r\n\t{\r\n\t\timpl::xml_buffered_writer buffered_writer(writer, encoding);\r\n\r\n\t\tif ((flags & format_write_bom) && encoding != encoding_latin1)\r\n\t\t{\r\n\t\t\t// BOM always represents the codepoint U+FEFF, so just write it in native encoding\r\n\t\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\t\tunsigned int bom = 0xfeff;\r\n\t\t\tbuffered_writer.write(static_cast<wchar_t>(bom));\r\n\t\t#else\r\n\t\t\tbuffered_writer.write('\\xef', '\\xbb', '\\xbf');\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\tif (!(flags & format_no_declaration) && !impl::has_declaration(_root))\r\n\t\t{\r\n\t\t\tbuffered_writer.write_string(PUGIXML_TEXT(\"<?xml version=\\\"1.0\\\"\"));\r\n\t\t\tif (encoding == encoding_latin1) buffered_writer.write_string(PUGIXML_TEXT(\" encoding=\\\"ISO-8859-1\\\"\"));\r\n\t\t\tbuffered_writer.write('?', '>');\r\n\t\t\tif (!(flags & format_raw)) buffered_writer.write('\\n');\r\n\t\t}\r\n\r\n\t\timpl::node_output(buffered_writer, _root, indent, flags, 0);\r\n\r\n\t\tbuffered_writer.flush();\r\n\t}\r\n\r\n#ifndef PUGIXML_NO_STL\r\n\tPUGI_IMPL_FN void xml_document::save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const\r\n\t{\r\n\t\txml_writer_stream writer(stream);\r\n\r\n\t\tsave(writer, indent, flags, encoding);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xml_document::save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags) const\r\n\t{\r\n\t\txml_writer_stream writer(stream);\r\n\r\n\t\tsave(writer, indent, flags, encoding_wchar);\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const\r\n\t{\r\n\t\tusing impl::auto_deleter; // MSVC7 workaround\r\n\t\tauto_deleter<FILE> file(impl::open_file(path_, (flags & format_save_file_text) ? \"w\" : \"wb\"), impl::close_file);\r\n\r\n\t\treturn impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const\r\n\t{\r\n\t\tusing impl::auto_deleter; // MSVC7 workaround\r\n\t\tauto_deleter<FILE> file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L\"w\" : L\"wb\"), impl::close_file);\r\n\r\n\t\treturn impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xml_document::document_element() const\r\n\t{\r\n\t\tassert(_root);\r\n\r\n\t\tfor (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)\r\n\t\t\tif (PUGI_IMPL_NODETYPE(i) == node_element)\r\n\t\t\t\treturn xml_node(i);\r\n\r\n\t\treturn xml_node();\r\n\t}\r\n\r\n#ifndef PUGIXML_NO_STL\r\n\tPUGI_IMPL_FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str)\r\n\t{\r\n\t\tassert(str);\r\n\r\n\t\treturn impl::as_utf8_impl(str, impl::strlength_wide(str));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t>& str)\r\n\t{\r\n\t\treturn impl::as_utf8_impl(str.c_str(), str.size());\r\n\t}\r\n\r\n\tPUGI_IMPL_FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const char* str)\r\n\t{\r\n\t\tassert(str);\r\n\r\n\t\treturn impl::as_wide_impl(str, strlen(str));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const std::string& str)\r\n\t{\r\n\t\treturn impl::as_wide_impl(str.c_str(), str.size());\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate)\r\n\t{\r\n\t\timpl::xml_memory::allocate = allocate;\r\n\t\timpl::xml_memory::deallocate = deallocate;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function()\r\n\t{\r\n\t\treturn impl::xml_memory::allocate;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function()\r\n\t{\r\n\t\treturn impl::xml_memory::deallocate;\r\n\t}\r\n}\r\n\r\n#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))\r\nnamespace std\r\n{\r\n\t// Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)\r\n\tPUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&)\r\n\t{\r\n\t\treturn std::bidirectional_iterator_tag();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&)\r\n\t{\r\n\t\treturn std::bidirectional_iterator_tag();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&)\r\n\t{\r\n\t\treturn std::bidirectional_iterator_tag();\r\n\t}\r\n}\r\n#endif\r\n\r\n#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)\r\nnamespace std\r\n{\r\n\t// Workarounds for (non-standard) iterator category detection\r\n\tPUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&)\r\n\t{\r\n\t\treturn std::bidirectional_iterator_tag();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&)\r\n\t{\r\n\t\treturn std::bidirectional_iterator_tag();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&)\r\n\t{\r\n\t\treturn std::bidirectional_iterator_tag();\r\n\t}\r\n}\r\n#endif\r\n\r\n#ifndef PUGIXML_NO_XPATH\r\n// STL replacements\r\nPUGI_IMPL_NS_BEGIN\r\n\tstruct equal_to\r\n\t{\r\n\t\ttemplate <typename T> bool operator()(const T& lhs, const T& rhs) const\r\n\t\t{\r\n\t\t\treturn lhs == rhs;\r\n\t\t}\r\n\t};\r\n\r\n\tstruct not_equal_to\r\n\t{\r\n\t\ttemplate <typename T> bool operator()(const T& lhs, const T& rhs) const\r\n\t\t{\r\n\t\t\treturn lhs != rhs;\r\n\t\t}\r\n\t};\r\n\r\n\tstruct less\r\n\t{\r\n\t\ttemplate <typename T> bool operator()(const T& lhs, const T& rhs) const\r\n\t\t{\r\n\t\t\treturn lhs < rhs;\r\n\t\t}\r\n\t};\r\n\r\n\tstruct less_equal\r\n\t{\r\n\t\ttemplate <typename T> bool operator()(const T& lhs, const T& rhs) const\r\n\t\t{\r\n\t\t\treturn lhs <= rhs;\r\n\t\t}\r\n\t};\r\n\r\n\ttemplate <typename T> inline void swap(T& lhs, T& rhs)\r\n\t{\r\n\t\tT temp = lhs;\r\n\t\tlhs = rhs;\r\n\t\trhs = temp;\r\n\t}\r\n\r\n\ttemplate <typename I, typename Pred> PUGI_IMPL_FN I min_element(I begin, I end, const Pred& pred)\r\n\t{\r\n\t\tI result = begin;\r\n\r\n\t\tfor (I it = begin + 1; it != end; ++it)\r\n\t\t\tif (pred(*it, *result))\r\n\t\t\t\tresult = it;\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\ttemplate <typename I> PUGI_IMPL_FN void reverse(I begin, I end)\r\n\t{\r\n\t\twhile (end - begin > 1)\r\n\t\t\tswap(*begin++, *--end);\r\n\t}\r\n\r\n\ttemplate <typename I> PUGI_IMPL_FN I unique(I begin, I end)\r\n\t{\r\n\t\t// fast skip head\r\n\t\twhile (end - begin > 1 && *begin != *(begin + 1))\r\n\t\t\tbegin++;\r\n\r\n\t\tif (begin == end)\r\n\t\t\treturn begin;\r\n\r\n\t\t// last written element\r\n\t\tI write = begin++;\r\n\r\n\t\t// merge unique elements\r\n\t\twhile (begin != end)\r\n\t\t{\r\n\t\t\tif (*begin != *write)\r\n\t\t\t\t*++write = *begin++;\r\n\t\t\telse\r\n\t\t\t\tbegin++;\r\n\t\t}\r\n\r\n\t\t// past-the-end (write points to live element)\r\n\t\treturn write + 1;\r\n\t}\r\n\r\n\ttemplate <typename T, typename Pred> PUGI_IMPL_FN void insertion_sort(T* begin, T* end, const Pred& pred)\r\n\t{\r\n\t\tif (begin == end)\r\n\t\t\treturn;\r\n\r\n\t\tfor (T* it = begin + 1; it != end; ++it)\r\n\t\t{\r\n\t\t\tT val = *it;\r\n\t\t\tT* hole = it;\r\n\r\n\t\t\t// move hole backwards\r\n\t\t\twhile (hole > begin && pred(val, *(hole - 1)))\r\n\t\t\t{\r\n\t\t\t\t*hole = *(hole - 1);\r\n\t\t\t\thole--;\r\n\t\t\t}\r\n\r\n\t\t\t// fill hole with element\r\n\t\t\t*hole = val;\r\n\t\t}\r\n\t}\r\n\r\n\ttemplate <typename I, typename Pred> inline I median3(I first, I middle, I last, const Pred& pred)\r\n\t{\r\n\t\tif (pred(*middle, *first))\r\n\t\t\tswap(middle, first);\r\n\t\tif (pred(*last, *middle))\r\n\t\t\tswap(last, middle);\r\n\t\tif (pred(*middle, *first))\r\n\t\t\tswap(middle, first);\r\n\r\n\t\treturn middle;\r\n\t}\r\n\r\n\ttemplate <typename T, typename Pred> PUGI_IMPL_FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend)\r\n\t{\r\n\t\t// invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups)\r\n\t\tT* eq = begin;\r\n\t\tT* lt = begin;\r\n\t\tT* gt = end;\r\n\r\n\t\twhile (lt < gt)\r\n\t\t{\r\n\t\t\tif (pred(*lt, pivot))\r\n\t\t\t\tlt++;\r\n\t\t\telse if (*lt == pivot)\r\n\t\t\t\tswap(*eq++, *lt++);\r\n\t\t\telse\r\n\t\t\t\tswap(*lt, *--gt);\r\n\t\t}\r\n\r\n\t\t// we now have just 4 groups: = < >; move equal elements to the middle\r\n\t\tT* eqbeg = gt;\r\n\r\n\t\tfor (T* it = begin; it != eq; ++it)\r\n\t\t\tswap(*it, *--eqbeg);\r\n\r\n\t\t*out_eqbeg = eqbeg;\r\n\t\t*out_eqend = gt;\r\n\t}\r\n\r\n\ttemplate <typename I, typename Pred> PUGI_IMPL_FN void sort(I begin, I end, const Pred& pred)\r\n\t{\r\n\t\t// sort large chunks\r\n\t\twhile (end - begin > 16)\r\n\t\t{\r\n\t\t\t// find median element\r\n\t\t\tI middle = begin + (end - begin) / 2;\r\n\t\t\tI median = median3(begin, middle, end - 1, pred);\r\n\r\n\t\t\t// partition in three chunks (< = >)\r\n\t\t\tI eqbeg, eqend;\r\n\t\t\tpartition3(begin, end, *median, pred, &eqbeg, &eqend);\r\n\r\n\t\t\t// loop on larger half\r\n\t\t\tif (eqbeg - begin > end - eqend)\r\n\t\t\t{\r\n\t\t\t\tsort(eqend, end, pred);\r\n\t\t\t\tend = eqbeg;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tsort(begin, eqbeg, pred);\r\n\t\t\t\tbegin = eqend;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// insertion sort small chunk\r\n\t\tinsertion_sort(begin, end, pred);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool hash_insert(const void** table, size_t size, const void* key)\r\n\t{\r\n\t\tassert(key);\r\n\r\n\t\tunsigned int h = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(key));\r\n\r\n\t\t// MurmurHash3 32-bit finalizer\r\n\t\th ^= h >> 16;\r\n\t\th *= 0x85ebca6bu;\r\n\t\th ^= h >> 13;\r\n\t\th *= 0xc2b2ae35u;\r\n\t\th ^= h >> 16;\r\n\r\n\t\tsize_t hashmod = size - 1;\r\n\t\tsize_t bucket = h & hashmod;\r\n\r\n\t\tfor (size_t probe = 0; probe <= hashmod; ++probe)\r\n\t\t{\r\n\t\t\tif (table[bucket] == 0)\r\n\t\t\t{\r\n\t\t\t\ttable[bucket] = key;\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\r\n\t\t\tif (table[bucket] == key)\r\n\t\t\t\treturn false;\r\n\r\n\t\t\t// hash collision, quadratic probing\r\n\t\t\tbucket = (bucket + probe + 1) & hashmod;\r\n\t\t}\r\n\r\n\t\tassert(false && \"Hash table is full\"); // unreachable\r\n\t\treturn false;\r\n\t}\r\nPUGI_IMPL_NS_END\r\n\r\n// Allocator used for AST and evaluation stacks\r\nPUGI_IMPL_NS_BEGIN\r\n\tstatic const size_t xpath_memory_page_size =\r\n\t#ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE\r\n\t\tPUGIXML_MEMORY_XPATH_PAGE_SIZE\r\n\t#else\r\n\t\t4096\r\n\t#endif\r\n\t\t;\r\n\r\n\tstatic const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*);\r\n\r\n\tstruct xpath_memory_block\r\n\t{\r\n\t\txpath_memory_block* next;\r\n\t\tsize_t capacity;\r\n\r\n\t\tunion\r\n\t\t{\r\n\t\t\tchar data[xpath_memory_page_size];\r\n\t\t\tdouble alignment;\r\n\t\t};\r\n\t};\r\n\r\n\tstruct xpath_allocator\r\n\t{\r\n\t\txpath_memory_block* _root;\r\n\t\tsize_t _root_size;\r\n\t\tbool* _error;\r\n\r\n\t\txpath_allocator(xpath_memory_block* root, bool* error = 0): _root(root), _root_size(0), _error(error)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tvoid* allocate(size_t size)\r\n\t\t{\r\n\t\t\t// round size up to block alignment boundary\r\n\t\t\tsize = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);\r\n\r\n\t\t\tif (_root_size + size <= _root->capacity)\r\n\t\t\t{\r\n\t\t\t\tvoid* buf = &_root->data[0] + _root_size;\r\n\t\t\t\t_root_size += size;\r\n\t\t\t\treturn buf;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests\r\n\t\t\t\tsize_t block_capacity_base = sizeof(_root->data);\r\n\t\t\t\tsize_t block_capacity_req = size + block_capacity_base / 4;\r\n\t\t\t\tsize_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req;\r\n\r\n\t\t\t\tsize_t block_size = block_capacity + offsetof(xpath_memory_block, data);\r\n\r\n\t\t\t\txpath_memory_block* block = static_cast<xpath_memory_block*>(xml_memory::allocate(block_size));\r\n\t\t\t\tif (!block)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (_error) *_error = true;\r\n\t\t\t\t\treturn 0;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tblock->next = _root;\r\n\t\t\t\tblock->capacity = block_capacity;\r\n\r\n\t\t\t\t_root = block;\r\n\t\t\t\t_root_size = size;\r\n\r\n\t\t\t\treturn block->data;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvoid* reallocate(void* ptr, size_t old_size, size_t new_size)\r\n\t\t{\r\n\t\t\t// round size up to block alignment boundary\r\n\t\t\told_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);\r\n\t\t\tnew_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);\r\n\r\n\t\t\t// we can only reallocate the last object\r\n\t\t\tassert(ptr == 0 || static_cast<char*>(ptr) + old_size == &_root->data[0] + _root_size);\r\n\r\n\t\t\t// try to reallocate the object inplace\r\n\t\t\tif (ptr && _root_size - old_size + new_size <= _root->capacity)\r\n\t\t\t{\r\n\t\t\t\t_root_size = _root_size - old_size + new_size;\r\n\t\t\t\treturn ptr;\r\n\t\t\t}\r\n\r\n\t\t\t// allocate a new block\r\n\t\t\tvoid* result = allocate(new_size);\r\n\t\t\tif (!result) return 0;\r\n\r\n\t\t\t// we have a new block\r\n\t\t\tif (ptr)\r\n\t\t\t{\r\n\t\t\t\t// copy old data (we only support growing)\r\n\t\t\t\tassert(new_size >= old_size);\r\n\t\t\t\tmemcpy(result, ptr, old_size);\r\n\r\n\t\t\t\t// free the previous page if it had no other objects\r\n\t\t\t\tassert(_root->data == result);\r\n\t\t\t\tassert(_root->next);\r\n\r\n\t\t\t\tif (_root->next->data == ptr)\r\n\t\t\t\t{\r\n\t\t\t\t\t// deallocate the whole page, unless it was the first one\r\n\t\t\t\t\txpath_memory_block* next = _root->next->next;\r\n\r\n\t\t\t\t\tif (next)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\txml_memory::deallocate(_root->next);\r\n\t\t\t\t\t\t_root->next = next;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\treturn result;\r\n\t\t}\r\n\r\n\t\tvoid revert(const xpath_allocator& state)\r\n\t\t{\r\n\t\t\t// free all new pages\r\n\t\t\txpath_memory_block* cur = _root;\r\n\r\n\t\t\twhile (cur != state._root)\r\n\t\t\t{\r\n\t\t\t\txpath_memory_block* next = cur->next;\r\n\r\n\t\t\t\txml_memory::deallocate(cur);\r\n\r\n\t\t\t\tcur = next;\r\n\t\t\t}\r\n\r\n\t\t\t// restore state\r\n\t\t\t_root = state._root;\r\n\t\t\t_root_size = state._root_size;\r\n\t\t}\r\n\r\n\t\tvoid release()\r\n\t\t{\r\n\t\t\txpath_memory_block* cur = _root;\r\n\t\t\tassert(cur);\r\n\r\n\t\t\twhile (cur->next)\r\n\t\t\t{\r\n\t\t\t\txpath_memory_block* next = cur->next;\r\n\r\n\t\t\t\txml_memory::deallocate(cur);\r\n\r\n\t\t\t\tcur = next;\r\n\t\t\t}\r\n\t\t}\r\n\t};\r\n\r\n\tstruct xpath_allocator_capture\r\n\t{\r\n\t\txpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\t~xpath_allocator_capture()\r\n\t\t{\r\n\t\t\t_target->revert(_state);\r\n\t\t}\r\n\r\n\t\txpath_allocator* _target;\r\n\t\txpath_allocator _state;\r\n\t};\r\n\r\n\tstruct xpath_stack\r\n\t{\r\n\t\txpath_allocator* result;\r\n\t\txpath_allocator* temp;\r\n\t};\r\n\r\n\tstruct xpath_stack_data\r\n\t{\r\n\t\txpath_memory_block blocks[2];\r\n\t\txpath_allocator result;\r\n\t\txpath_allocator temp;\r\n\t\txpath_stack stack;\r\n\t\tbool oom;\r\n\r\n\t\txpath_stack_data(): result(blocks + 0, &oom), temp(blocks + 1, &oom), oom(false)\r\n\t\t{\r\n\t\t\tblocks[0].next = blocks[1].next = 0;\r\n\t\t\tblocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data);\r\n\r\n\t\t\tstack.result = &result;\r\n\t\t\tstack.temp = &temp;\r\n\t\t}\r\n\r\n\t\t~xpath_stack_data()\r\n\t\t{\r\n\t\t\tresult.release();\r\n\t\t\ttemp.release();\r\n\t\t}\r\n\t};\r\nPUGI_IMPL_NS_END\r\n\r\n// String class\r\nPUGI_IMPL_NS_BEGIN\r\n\tclass xpath_string\r\n\t{\r\n\t\tconst char_t* _buffer;\r\n\t\tbool _uses_heap;\r\n\t\tsize_t _length_heap;\r\n\r\n\t\tstatic char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc)\r\n\t\t{\r\n\t\t\tchar_t* result = static_cast<char_t*>(alloc->allocate((length + 1) * sizeof(char_t)));\r\n\t\t\tif (!result) return 0;\r\n\r\n\t\t\tmemcpy(result, string, length * sizeof(char_t));\r\n\t\t\tresult[length] = 0;\r\n\r\n\t\t\treturn result;\r\n\t\t}\r\n\r\n\t\txpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap)\r\n\t\t{\r\n\t\t}\r\n\r\n\tpublic:\r\n\t\tstatic xpath_string from_const(const char_t* str)\r\n\t\t{\r\n\t\t\treturn xpath_string(str, false, 0);\r\n\t\t}\r\n\r\n\t\tstatic xpath_string from_heap_preallocated(const char_t* begin, const char_t* end)\r\n\t\t{\r\n\t\t\tassert(begin <= end && *end == 0);\r\n\r\n\t\t\treturn xpath_string(begin, true, static_cast<size_t>(end - begin));\r\n\t\t}\r\n\r\n\t\tstatic xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc)\r\n\t\t{\r\n\t\t\tassert(begin <= end);\r\n\r\n\t\t\tif (begin == end)\r\n\t\t\t\treturn xpath_string();\r\n\r\n\t\t\tsize_t length = static_cast<size_t>(end - begin);\r\n\t\t\tconst char_t* data = duplicate_string(begin, length, alloc);\r\n\r\n\t\t\treturn data ? xpath_string(data, true, length) : xpath_string();\r\n\t\t}\r\n\r\n\t\txpath_string(): _buffer(PUGIXML_TEXT(\"\")), _uses_heap(false), _length_heap(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tvoid append(const xpath_string& o, xpath_allocator* alloc)\r\n\t\t{\r\n\t\t\t// skip empty sources\r\n\t\t\tif (!*o._buffer) return;\r\n\r\n\t\t\t// fast append for constant empty target and constant source\r\n\t\t\tif (!*_buffer && !_uses_heap && !o._uses_heap)\r\n\t\t\t{\r\n\t\t\t\t_buffer = o._buffer;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t// need to make heap copy\r\n\t\t\t\tsize_t target_length = length();\r\n\t\t\t\tsize_t source_length = o.length();\r\n\t\t\t\tsize_t result_length = target_length + source_length;\r\n\r\n\t\t\t\t// allocate new buffer\r\n\t\t\t\tchar_t* result = static_cast<char_t*>(alloc->reallocate(_uses_heap ? const_cast<char_t*>(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t)));\r\n\t\t\t\tif (!result) return;\r\n\r\n\t\t\t\t// append first string to the new buffer in case there was no reallocation\r\n\t\t\t\tif (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t));\r\n\r\n\t\t\t\t// append second string to the new buffer\r\n\t\t\t\tmemcpy(result + target_length, o._buffer, source_length * sizeof(char_t));\r\n\t\t\t\tresult[result_length] = 0;\r\n\r\n\t\t\t\t// finalize\r\n\t\t\t\t_buffer = result;\r\n\t\t\t\t_uses_heap = true;\r\n\t\t\t\t_length_heap = result_length;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tconst char_t* c_str() const\r\n\t\t{\r\n\t\t\treturn _buffer;\r\n\t\t}\r\n\r\n\t\tsize_t length() const\r\n\t\t{\r\n\t\t\treturn _uses_heap ? _length_heap : strlength(_buffer);\r\n\t\t}\r\n\r\n\t\tchar_t* data(xpath_allocator* alloc)\r\n\t\t{\r\n\t\t\t// make private heap copy\r\n\t\t\tif (!_uses_heap)\r\n\t\t\t{\r\n\t\t\t\tsize_t length_ = strlength(_buffer);\r\n\t\t\t\tconst char_t* data_ = duplicate_string(_buffer, length_, alloc);\r\n\r\n\t\t\t\tif (!data_) return 0;\r\n\r\n\t\t\t\t_buffer = data_;\r\n\t\t\t\t_uses_heap = true;\r\n\t\t\t\t_length_heap = length_;\r\n\t\t\t}\r\n\r\n\t\t\treturn const_cast<char_t*>(_buffer);\r\n\t\t}\r\n\r\n\t\tbool empty() const\r\n\t\t{\r\n\t\t\treturn *_buffer == 0;\r\n\t\t}\r\n\r\n\t\tbool operator==(const xpath_string& o) const\r\n\t\t{\r\n\t\t\treturn strequal(_buffer, o._buffer);\r\n\t\t}\r\n\r\n\t\tbool operator!=(const xpath_string& o) const\r\n\t\t{\r\n\t\t\treturn !strequal(_buffer, o._buffer);\r\n\t\t}\r\n\r\n\t\tbool uses_heap() const\r\n\t\t{\r\n\t\t\treturn _uses_heap;\r\n\t\t}\r\n\t};\r\nPUGI_IMPL_NS_END\r\n\r\nPUGI_IMPL_NS_BEGIN\r\n\tPUGI_IMPL_FN bool starts_with(const char_t* string, const char_t* pattern)\r\n\t{\r\n\t\twhile (*pattern && *string == *pattern)\r\n\t\t{\r\n\t\t\tstring++;\r\n\t\t\tpattern++;\r\n\t\t}\r\n\r\n\t\treturn *pattern == 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* find_char(const char_t* s, char_t c)\r\n\t{\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\treturn wcschr(s, c);\r\n\t#else\r\n\t\treturn strchr(s, c);\r\n\t#endif\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* find_substring(const char_t* s, const char_t* p)\r\n\t{\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\t// MSVC6 wcsstr bug workaround (if s is empty it always returns 0)\r\n\t\treturn (*p == 0) ? s : wcsstr(s, p);\r\n\t#else\r\n\t\treturn strstr(s, p);\r\n\t#endif\r\n\t}\r\n\r\n\t// Converts symbol to lower case, if it is an ASCII one\r\n\tPUGI_IMPL_FN char_t tolower_ascii(char_t ch)\r\n\t{\r\n\t\treturn static_cast<unsigned int>(ch - 'A') < 26 ? static_cast<char_t>(ch | ' ') : ch;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc)\r\n\t{\r\n\t\tif (na.attribute())\r\n\t\t\treturn xpath_string::from_const(na.attribute().value());\r\n\t\telse\r\n\t\t{\r\n\t\t\txml_node n = na.node();\r\n\r\n\t\t\tswitch (n.type())\r\n\t\t\t{\r\n\t\t\tcase node_pcdata:\r\n\t\t\tcase node_cdata:\r\n\t\t\tcase node_comment:\r\n\t\t\tcase node_pi:\r\n\t\t\t\treturn xpath_string::from_const(n.value());\r\n\r\n\t\t\tcase node_document:\r\n\t\t\tcase node_element:\r\n\t\t\t{\r\n\t\t\t\txpath_string result;\r\n\r\n\t\t\t\t// element nodes can have value if parse_embed_pcdata was used\r\n\t\t\t\tif (n.value()[0])\r\n\t\t\t\t\tresult.append(xpath_string::from_const(n.value()), alloc);\r\n\r\n\t\t\t\txml_node cur = n.first_child();\r\n\r\n\t\t\t\twhile (cur && cur != n)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (cur.type() == node_pcdata || cur.type() == node_cdata)\r\n\t\t\t\t\t\tresult.append(xpath_string::from_const(cur.value()), alloc);\r\n\r\n\t\t\t\t\tif (cur.first_child())\r\n\t\t\t\t\t\tcur = cur.first_child();\r\n\t\t\t\t\telse if (cur.next_sibling())\r\n\t\t\t\t\t\tcur = cur.next_sibling();\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\twhile (!cur.next_sibling() && cur != n)\r\n\t\t\t\t\t\t\tcur = cur.parent();\r\n\r\n\t\t\t\t\t\tif (cur != n) cur = cur.next_sibling();\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn result;\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\treturn xpath_string();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn)\r\n\t{\r\n\t\tassert(ln->parent == rn->parent);\r\n\r\n\t\t// there is no common ancestor (the shared parent is null), nodes are from different documents\r\n\t\tif (!ln->parent) return ln < rn;\r\n\r\n\t\t// determine sibling order\r\n\t\txml_node_struct* ls = ln;\r\n\t\txml_node_struct* rs = rn;\r\n\r\n\t\twhile (ls && rs)\r\n\t\t{\r\n\t\t\tif (ls == rn) return true;\r\n\t\t\tif (rs == ln) return false;\r\n\r\n\t\t\tls = ls->next_sibling;\r\n\t\t\trs = rs->next_sibling;\r\n\t\t}\r\n\r\n\t\t// if rn sibling chain ended ln must be before rn\r\n\t\treturn !rs;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn)\r\n\t{\r\n\t\t// find common ancestor at the same depth, if any\r\n\t\txml_node_struct* lp = ln;\r\n\t\txml_node_struct* rp = rn;\r\n\r\n\t\twhile (lp && rp && lp->parent != rp->parent)\r\n\t\t{\r\n\t\t\tlp = lp->parent;\r\n\t\t\trp = rp->parent;\r\n\t\t}\r\n\r\n\t\t// parents are the same!\r\n\t\tif (lp && rp) return node_is_before_sibling(lp, rp);\r\n\r\n\t\t// nodes are at different depths, need to normalize heights\r\n\t\tbool left_higher = !lp;\r\n\r\n\t\twhile (lp)\r\n\t\t{\r\n\t\t\tlp = lp->parent;\r\n\t\t\tln = ln->parent;\r\n\t\t}\r\n\r\n\t\twhile (rp)\r\n\t\t{\r\n\t\t\trp = rp->parent;\r\n\t\t\trn = rn->parent;\r\n\t\t}\r\n\r\n\t\t// one node is the ancestor of the other\r\n\t\tif (ln == rn) return left_higher;\r\n\r\n\t\t// find common ancestor... again\r\n\t\twhile (ln->parent != rn->parent)\r\n\t\t{\r\n\t\t\tln = ln->parent;\r\n\t\t\trn = rn->parent;\r\n\t\t}\r\n\r\n\t\treturn node_is_before_sibling(ln, rn);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node)\r\n\t{\r\n\t\twhile (node && node != parent) node = node->parent;\r\n\r\n\t\treturn parent && node == parent;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const void* document_buffer_order(const xpath_node& xnode)\r\n\t{\r\n\t\txml_node_struct* node = xnode.node().internal_object();\r\n\r\n\t\tif (node)\r\n\t\t{\r\n\t\t\tif ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0)\r\n\t\t\t{\r\n\t\t\t\tif (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name;\r\n\t\t\t\tif (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value;\r\n\t\t\t}\r\n\r\n\t\t\treturn 0;\r\n\t\t}\r\n\r\n\t\txml_attribute_struct* attr = xnode.attribute().internal_object();\r\n\r\n\t\tif (attr)\r\n\t\t{\r\n\t\t\tif ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0)\r\n\t\t\t{\r\n\t\t\t\tif ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name;\r\n\t\t\t\tif ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value;\r\n\t\t\t}\r\n\r\n\t\t\treturn 0;\r\n\t\t}\r\n\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tstruct document_order_comparator\r\n\t{\r\n\t\tbool operator()(const xpath_node& lhs, const xpath_node& rhs) const\r\n\t\t{\r\n\t\t\t// optimized document order based check\r\n\t\t\tconst void* lo = document_buffer_order(lhs);\r\n\t\t\tconst void* ro = document_buffer_order(rhs);\r\n\r\n\t\t\tif (lo && ro) return lo < ro;\r\n\r\n\t\t\t// slow comparison\r\n\t\t\txml_node ln = lhs.node(), rn = rhs.node();\r\n\r\n\t\t\t// compare attributes\r\n\t\t\tif (lhs.attribute() && rhs.attribute())\r\n\t\t\t{\r\n\t\t\t\t// shared parent\r\n\t\t\t\tif (lhs.parent() == rhs.parent())\r\n\t\t\t\t{\r\n\t\t\t\t\t// determine sibling order\r\n\t\t\t\t\tfor (xml_attribute a = lhs.attribute(); a; a = a.next_attribute())\r\n\t\t\t\t\t\tif (a == rhs.attribute())\r\n\t\t\t\t\t\t\treturn true;\r\n\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// compare attribute parents\r\n\t\t\t\tln = lhs.parent();\r\n\t\t\t\trn = rhs.parent();\r\n\t\t\t}\r\n\t\t\telse if (lhs.attribute())\r\n\t\t\t{\r\n\t\t\t\t// attributes go after the parent element\r\n\t\t\t\tif (lhs.parent() == rhs.node()) return false;\r\n\r\n\t\t\t\tln = lhs.parent();\r\n\t\t\t}\r\n\t\t\telse if (rhs.attribute())\r\n\t\t\t{\r\n\t\t\t\t// attributes go after the parent element\r\n\t\t\t\tif (rhs.parent() == lhs.node()) return true;\r\n\r\n\t\t\t\trn = rhs.parent();\r\n\t\t\t}\r\n\r\n\t\t\tif (ln == rn) return false;\r\n\r\n\t\t\tif (!ln || !rn) return ln < rn;\r\n\r\n\t\t\treturn node_is_before(ln.internal_object(), rn.internal_object());\r\n\t\t}\r\n\t};\r\n\r\n\tPUGI_IMPL_FN double gen_nan()\r\n\t{\r\n\t#if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24))\r\n\t\tPUGI_IMPL_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t));\r\n\t\ttypedef uint32_t UI; // BCC5 workaround\r\n\t\tunion { float f; UI i; } u;\r\n\t\tu.i = 0x7fc00000;\r\n\t\treturn double(u.f);\r\n\t#else\r\n\t\t// fallback\r\n\t\tconst volatile double zero = 0.0;\r\n\t\treturn zero / zero;\r\n\t#endif\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool is_nan(double value)\r\n\t{\r\n\t#if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__)\r\n\t\treturn !!_isnan(value);\r\n\t#elif defined(fpclassify) && defined(FP_NAN)\r\n\t\treturn fpclassify(value) == FP_NAN;\r\n\t#else\r\n\t\t// fallback\r\n\t\tconst volatile double v = value;\r\n\t\treturn v != v;\r\n\t#endif\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* convert_number_to_string_special(double value)\r\n\t{\r\n\t#if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__)\r\n\t\tif (_finite(value)) return (value == 0) ? PUGIXML_TEXT(\"0\") : 0;\r\n\t\tif (_isnan(value)) return PUGIXML_TEXT(\"NaN\");\r\n\t\treturn value > 0 ? PUGIXML_TEXT(\"Infinity\") : PUGIXML_TEXT(\"-Infinity\");\r\n\t#elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO)\r\n\t\tswitch (fpclassify(value))\r\n\t\t{\r\n\t\tcase FP_NAN:\r\n\t\t\treturn PUGIXML_TEXT(\"NaN\");\r\n\r\n\t\tcase FP_INFINITE:\r\n\t\t\treturn value > 0 ? PUGIXML_TEXT(\"Infinity\") : PUGIXML_TEXT(\"-Infinity\");\r\n\r\n\t\tcase FP_ZERO:\r\n\t\t\treturn PUGIXML_TEXT(\"0\");\r\n\r\n\t\tdefault:\r\n\t\t\treturn 0;\r\n\t\t}\r\n\t#else\r\n\t\t// fallback\r\n\t\tconst volatile double v = value;\r\n\r\n\t\tif (v == 0) return PUGIXML_TEXT(\"0\");\r\n\t\tif (v != v) return PUGIXML_TEXT(\"NaN\");\r\n\t\tif (v * 2 == v) return value > 0 ? PUGIXML_TEXT(\"Infinity\") : PUGIXML_TEXT(\"-Infinity\");\r\n\t\treturn 0;\r\n\t#endif\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool convert_number_to_boolean(double value)\r\n\t{\r\n\t\treturn (value != 0 && !is_nan(value));\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void truncate_zeros(char* begin, char* end)\r\n\t{\r\n\t\twhile (begin != end && end[-1] == '0') end--;\r\n\r\n\t\t*end = 0;\r\n\t}\r\n\r\n\t// gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent\r\n#if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400\r\n\tPUGI_IMPL_FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)\r\n\t{\r\n\t\t// get base values\r\n\t\tint sign, exponent;\r\n\t\t_ecvt_s(buffer, sizeof(buffer), value, DBL_DIG + 1, &exponent, &sign);\r\n\r\n\t\t// truncate redundant zeros\r\n\t\ttruncate_zeros(buffer, buffer + strlen(buffer));\r\n\r\n\t\t// fill results\r\n\t\t*out_mantissa = buffer;\r\n\t\t*out_exponent = exponent;\r\n\t}\r\n#else\r\n\tPUGI_IMPL_FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)\r\n\t{\r\n\t\t// get a scientific notation value with IEEE DBL_DIG decimals\r\n\t\tPUGI_IMPL_SNPRINTF(buffer, \"%.*e\", DBL_DIG, value);\r\n\r\n\t\t// get the exponent (possibly negative)\r\n\t\tchar* exponent_string = strchr(buffer, 'e');\r\n\t\tassert(exponent_string);\r\n\r\n\t\tint exponent = atoi(exponent_string + 1);\r\n\r\n\t\t// extract mantissa string: skip sign\r\n\t\tchar* mantissa = buffer[0] == '-' ? buffer + 1 : buffer;\r\n\t\tassert(mantissa[0] != '0' && (mantissa[1] == '.' || mantissa[1] == ','));\r\n\r\n\t\t// divide mantissa by 10 to eliminate integer part\r\n\t\tmantissa[1] = mantissa[0];\r\n\t\tmantissa++;\r\n\t\texponent++;\r\n\r\n\t\t// remove extra mantissa digits and zero-terminate mantissa\r\n\t\ttruncate_zeros(mantissa, exponent_string);\r\n\r\n\t\t// fill results\r\n\t\t*out_mantissa = mantissa;\r\n\t\t*out_exponent = exponent;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc)\r\n\t{\r\n\t\t// try special number conversion\r\n\t\tconst char_t* special = convert_number_to_string_special(value);\r\n\t\tif (special) return xpath_string::from_const(special);\r\n\r\n\t\t// get mantissa + exponent form\r\n\t\tchar mantissa_buffer[32];\r\n\r\n\t\tchar* mantissa;\r\n\t\tint exponent;\r\n\t\tconvert_number_to_mantissa_exponent(value, mantissa_buffer, &mantissa, &exponent);\r\n\r\n\t\t// allocate a buffer of suitable length for the number\r\n\t\tsize_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4;\r\n\t\tchar_t* result = static_cast<char_t*>(alloc->allocate(sizeof(char_t) * result_size));\r\n\t\tif (!result) return xpath_string();\r\n\r\n\t\t// make the number!\r\n\t\tchar_t* s = result;\r\n\r\n\t\t// sign\r\n\t\tif (value < 0) *s++ = '-';\r\n\r\n\t\t// integer part\r\n\t\tif (exponent <= 0)\r\n\t\t{\r\n\t\t\t*s++ = '0';\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\twhile (exponent > 0)\r\n\t\t\t{\r\n\t\t\t\tassert(*mantissa == 0 || static_cast<unsigned int>(*mantissa - '0') <= 9);\r\n\t\t\t\t*s++ = *mantissa ? *mantissa++ : '0';\r\n\t\t\t\texponent--;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// fractional part\r\n\t\tif (*mantissa)\r\n\t\t{\r\n\t\t\t// decimal point\r\n\t\t\t*s++ = '.';\r\n\r\n\t\t\t// extra zeroes from negative exponent\r\n\t\t\twhile (exponent < 0)\r\n\t\t\t{\r\n\t\t\t\t*s++ = '0';\r\n\t\t\t\texponent++;\r\n\t\t\t}\r\n\r\n\t\t\t// extra mantissa digits\r\n\t\t\twhile (*mantissa)\r\n\t\t\t{\r\n\t\t\t\tassert(static_cast<unsigned int>(*mantissa - '0') <= 9);\r\n\t\t\t\t*s++ = *mantissa++;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// zero-terminate\r\n\t\tassert(s < result + result_size);\r\n\t\t*s = 0;\r\n\r\n\t\treturn xpath_string::from_heap_preallocated(result, s);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool check_string_to_number_format(const char_t* string)\r\n\t{\r\n\t\t// parse leading whitespace\r\n\t\twhile (PUGI_IMPL_IS_CHARTYPE(*string, ct_space)) ++string;\r\n\r\n\t\t// parse sign\r\n\t\tif (*string == '-') ++string;\r\n\r\n\t\tif (!*string) return false;\r\n\r\n\t\t// if there is no integer part, there should be a decimal part with at least one digit\r\n\t\tif (!PUGI_IMPL_IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI_IMPL_IS_CHARTYPEX(string[1], ctx_digit))) return false;\r\n\r\n\t\t// parse integer part\r\n\t\twhile (PUGI_IMPL_IS_CHARTYPEX(*string, ctx_digit)) ++string;\r\n\r\n\t\t// parse decimal part\r\n\t\tif (*string == '.')\r\n\t\t{\r\n\t\t\t++string;\r\n\r\n\t\t\twhile (PUGI_IMPL_IS_CHARTYPEX(*string, ctx_digit)) ++string;\r\n\t\t}\r\n\r\n\t\t// parse trailing whitespace\r\n\t\twhile (PUGI_IMPL_IS_CHARTYPE(*string, ct_space)) ++string;\r\n\r\n\t\treturn *string == 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN double convert_string_to_number(const char_t* string)\r\n\t{\r\n\t\t// check string format\r\n\t\tif (!check_string_to_number_format(string)) return gen_nan();\r\n\r\n\t\t// parse string\r\n\t#ifdef PUGIXML_WCHAR_MODE\r\n\t\treturn wcstod(string, 0);\r\n\t#else\r\n\t\treturn strtod(string, 0);\r\n\t#endif\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result)\r\n\t{\r\n\t\tsize_t length = static_cast<size_t>(end - begin);\r\n\t\tchar_t* scratch = buffer;\r\n\r\n\t\tif (length >= sizeof(buffer) / sizeof(buffer[0]))\r\n\t\t{\r\n\t\t\t// need to make dummy on-heap copy\r\n\t\t\tscratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));\r\n\t\t\tif (!scratch) return false;\r\n\t\t}\r\n\r\n\t\t// copy string to zero-terminated buffer and perform conversion\r\n\t\tmemcpy(scratch, begin, length * sizeof(char_t));\r\n\t\tscratch[length] = 0;\r\n\r\n\t\t*out_result = convert_string_to_number(scratch);\r\n\r\n\t\t// free dummy buffer\r\n\t\tif (scratch != buffer) xml_memory::deallocate(scratch);\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN double round_nearest(double value)\r\n\t{\r\n\t\treturn floor(value + 0.5);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN double round_nearest_nzero(double value)\r\n\t{\r\n\t\t// same as round_nearest, but returns -0 for [-0.5, -0]\r\n\t\t// ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0)\r\n\t\treturn (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* qualified_name(const xpath_node& node)\r\n\t{\r\n\t\treturn node.attribute() ? node.attribute().name() : node.node().name();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* local_name(const xpath_node& node)\r\n\t{\r\n\t\tconst char_t* name = qualified_name(node);\r\n\t\tconst char_t* p = find_char(name, ':');\r\n\r\n\t\treturn p ? p + 1 : name;\r\n\t}\r\n\r\n\tstruct namespace_uri_predicate\r\n\t{\r\n\t\tconst char_t* prefix;\r\n\t\tsize_t prefix_length;\r\n\r\n\t\tnamespace_uri_predicate(const char_t* name)\r\n\t\t{\r\n\t\t\tconst char_t* pos = find_char(name, ':');\r\n\r\n\t\t\tprefix = pos ? name : 0;\r\n\t\t\tprefix_length = pos ? static_cast<size_t>(pos - name) : 0;\r\n\t\t}\r\n\r\n\t\tbool operator()(xml_attribute a) const\r\n\t\t{\r\n\t\t\tconst char_t* name = a.name();\r\n\r\n\t\t\tif (!starts_with(name, PUGIXML_TEXT(\"xmlns\"))) return false;\r\n\r\n\t\t\treturn prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0;\r\n\t\t}\r\n\t};\r\n\r\n\tPUGI_IMPL_FN const char_t* namespace_uri(xml_node node)\r\n\t{\r\n\t\tnamespace_uri_predicate pred = node.name();\r\n\r\n\t\txml_node p = node;\r\n\r\n\t\twhile (p)\r\n\t\t{\r\n\t\t\txml_attribute a = p.find_attribute(pred);\r\n\r\n\t\t\tif (a) return a.value();\r\n\r\n\t\t\tp = p.parent();\r\n\t\t}\r\n\r\n\t\treturn PUGIXML_TEXT(\"\");\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* namespace_uri(xml_attribute attr, xml_node parent)\r\n\t{\r\n\t\tnamespace_uri_predicate pred = attr.name();\r\n\r\n\t\t// Default namespace does not apply to attributes\r\n\t\tif (!pred.prefix) return PUGIXML_TEXT(\"\");\r\n\r\n\t\txml_node p = parent;\r\n\r\n\t\twhile (p)\r\n\t\t{\r\n\t\t\txml_attribute a = p.find_attribute(pred);\r\n\r\n\t\t\tif (a) return a.value();\r\n\r\n\t\t\tp = p.parent();\r\n\t\t}\r\n\r\n\t\treturn PUGIXML_TEXT(\"\");\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* namespace_uri(const xpath_node& node)\r\n\t{\r\n\t\treturn node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node());\r\n\t}\r\n\r\n\tPUGI_IMPL_FN char_t* normalize_space(char_t* buffer)\r\n\t{\r\n\t\tchar_t* write = buffer;\r\n\r\n\t\tfor (char_t* it = buffer; *it; )\r\n\t\t{\r\n\t\t\tchar_t ch = *it++;\r\n\r\n\t\t\tif (PUGI_IMPL_IS_CHARTYPE(ch, ct_space))\r\n\t\t\t{\r\n\t\t\t\t// replace whitespace sequence with single space\r\n\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPE(*it, ct_space)) it++;\r\n\r\n\t\t\t\t// avoid leading spaces\r\n\t\t\t\tif (write != buffer) *write++ = ' ';\r\n\t\t\t}\r\n\t\t\telse *write++ = ch;\r\n\t\t}\r\n\r\n\t\t// remove trailing space\r\n\t\tif (write != buffer && PUGI_IMPL_IS_CHARTYPE(write[-1], ct_space)) write--;\r\n\r\n\t\t// zero-terminate\r\n\t\t*write = 0;\r\n\r\n\t\treturn write;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length)\r\n\t{\r\n\t\tchar_t* write = buffer;\r\n\r\n\t\twhile (*buffer)\r\n\t\t{\r\n\t\t\tPUGI_IMPL_DMC_VOLATILE char_t ch = *buffer++;\r\n\r\n\t\t\tconst char_t* pos = find_char(from, ch);\r\n\r\n\t\t\tif (!pos)\r\n\t\t\t\t*write++ = ch; // do not process\r\n\t\t\telse if (static_cast<size_t>(pos - from) < to_length)\r\n\t\t\t\t*write++ = to[pos - from]; // replace\r\n\t\t}\r\n\r\n\t\t// zero-terminate\r\n\t\t*write = 0;\r\n\r\n\t\treturn write;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to)\r\n\t{\r\n\t\tunsigned char table[128] = {0};\r\n\r\n\t\twhile (*from)\r\n\t\t{\r\n\t\t\tunsigned int fc = static_cast<unsigned int>(*from);\r\n\t\t\tunsigned int tc = static_cast<unsigned int>(*to);\r\n\r\n\t\t\tif (fc >= 128 || tc >= 128)\r\n\t\t\t\treturn 0;\r\n\r\n\t\t\t// code=128 means \"skip character\"\r\n\t\t\tif (!table[fc])\r\n\t\t\t\ttable[fc] = static_cast<unsigned char>(tc ? tc : 128);\r\n\r\n\t\t\tfrom++;\r\n\t\t\tif (tc) to++;\r\n\t\t}\r\n\r\n\t\tfor (int i = 0; i < 128; ++i)\r\n\t\t\tif (!table[i])\r\n\t\t\t\ttable[i] = static_cast<unsigned char>(i);\r\n\r\n\t\tvoid* result = alloc->allocate(sizeof(table));\r\n\t\tif (!result) return 0;\r\n\r\n\t\tmemcpy(result, table, sizeof(table));\r\n\r\n\t\treturn static_cast<unsigned char*>(result);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN char_t* translate_table(char_t* buffer, const unsigned char* table)\r\n\t{\r\n\t\tchar_t* write = buffer;\r\n\r\n\t\twhile (*buffer)\r\n\t\t{\r\n\t\t\tchar_t ch = *buffer++;\r\n\t\t\tunsigned int index = static_cast<unsigned int>(ch);\r\n\r\n\t\t\tif (index < 128)\r\n\t\t\t{\r\n\t\t\t\tunsigned char code = table[index];\r\n\r\n\t\t\t\t// code=128 means \"skip character\" (table size is 128 so 128 can be a special value)\r\n\t\t\t\t// this code skips these characters without extra branches\r\n\t\t\t\t*write = static_cast<char_t>(code);\r\n\t\t\t\twrite += 1 - (code >> 7);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t*write++ = ch;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// zero-terminate\r\n\t\t*write = 0;\r\n\r\n\t\treturn write;\r\n\t}\r\n\r\n\tinline bool is_xpath_attribute(const char_t* name)\r\n\t{\r\n\t\treturn !(starts_with(name, PUGIXML_TEXT(\"xmlns\")) && (name[5] == 0 || name[5] == ':'));\r\n\t}\r\n\r\n\tstruct xpath_variable_boolean: xpath_variable\r\n\t{\r\n\t\txpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tbool value;\r\n\t\tchar_t name[1];\r\n\t};\r\n\r\n\tstruct xpath_variable_number: xpath_variable\r\n\t{\r\n\t\txpath_variable_number(): xpath_variable(xpath_type_number), value(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tdouble value;\r\n\t\tchar_t name[1];\r\n\t};\r\n\r\n\tstruct xpath_variable_string: xpath_variable\r\n\t{\r\n\t\txpath_variable_string(): xpath_variable(xpath_type_string), value(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\t~xpath_variable_string()\r\n\t\t{\r\n\t\t\tif (value) xml_memory::deallocate(value);\r\n\t\t}\r\n\r\n\t\tchar_t* value;\r\n\t\tchar_t name[1];\r\n\t};\r\n\r\n\tstruct xpath_variable_node_set: xpath_variable\r\n\t{\r\n\t\txpath_variable_node_set(): xpath_variable(xpath_type_node_set)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\txpath_node_set value;\r\n\t\tchar_t name[1];\r\n\t};\r\n\r\n\tstatic const xpath_node_set dummy_node_set;\r\n\r\n\tPUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW unsigned int hash_string(const char_t* str)\r\n\t{\r\n\t\t// Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time)\r\n\t\tunsigned int result = 0;\r\n\r\n\t\twhile (*str)\r\n\t\t{\r\n\t\t\tresult += static_cast<unsigned int>(*str++);\r\n\t\t\tresult += result << 10;\r\n\t\t\tresult ^= result >> 6;\r\n\t\t}\r\n\r\n\t\tresult += result << 3;\r\n\t\tresult ^= result >> 11;\r\n\t\tresult += result << 15;\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\ttemplate <typename T> PUGI_IMPL_FN T* new_xpath_variable(const char_t* name)\r\n\t{\r\n\t\tsize_t length = strlength(name);\r\n\t\tif (length == 0) return 0; // empty variable names are invalid\r\n\r\n\t\t// $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters\r\n\t\tvoid* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t));\r\n\t\tif (!memory) return 0;\r\n\r\n\t\tT* result = new (memory) T();\r\n\r\n\t\tmemcpy(result->name, name, (length + 1) * sizeof(char_t));\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name)\r\n\t{\r\n\t\tswitch (type)\r\n\t\t{\r\n\t\tcase xpath_type_node_set:\r\n\t\t\treturn new_xpath_variable<xpath_variable_node_set>(name);\r\n\r\n\t\tcase xpath_type_number:\r\n\t\t\treturn new_xpath_variable<xpath_variable_number>(name);\r\n\r\n\t\tcase xpath_type_string:\r\n\t\t\treturn new_xpath_variable<xpath_variable_string>(name);\r\n\r\n\t\tcase xpath_type_boolean:\r\n\t\t\treturn new_xpath_variable<xpath_variable_boolean>(name);\r\n\r\n\t\tdefault:\r\n\t\t\treturn 0;\r\n\t\t}\r\n\t}\r\n\r\n\ttemplate <typename T> PUGI_IMPL_FN void delete_xpath_variable(T* var)\r\n\t{\r\n\t\tvar->~T();\r\n\t\txml_memory::deallocate(var);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var)\r\n\t{\r\n\t\tswitch (type)\r\n\t\t{\r\n\t\tcase xpath_type_node_set:\r\n\t\t\tdelete_xpath_variable(static_cast<xpath_variable_node_set*>(var));\r\n\t\t\tbreak;\r\n\r\n\t\tcase xpath_type_number:\r\n\t\t\tdelete_xpath_variable(static_cast<xpath_variable_number*>(var));\r\n\t\t\tbreak;\r\n\r\n\t\tcase xpath_type_string:\r\n\t\t\tdelete_xpath_variable(static_cast<xpath_variable_string*>(var));\r\n\t\t\tbreak;\r\n\r\n\t\tcase xpath_type_boolean:\r\n\t\t\tdelete_xpath_variable(static_cast<xpath_variable_boolean*>(var));\r\n\t\t\tbreak;\r\n\r\n\t\tdefault:\r\n\t\t\tassert(false && \"Invalid variable type\"); // unreachable\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs)\r\n\t{\r\n\t\tswitch (rhs->type())\r\n\t\t{\r\n\t\tcase xpath_type_node_set:\r\n\t\t\treturn lhs->set(static_cast<const xpath_variable_node_set*>(rhs)->value);\r\n\r\n\t\tcase xpath_type_number:\r\n\t\t\treturn lhs->set(static_cast<const xpath_variable_number*>(rhs)->value);\r\n\r\n\t\tcase xpath_type_string:\r\n\t\t\treturn lhs->set(static_cast<const xpath_variable_string*>(rhs)->value);\r\n\r\n\t\tcase xpath_type_boolean:\r\n\t\t\treturn lhs->set(static_cast<const xpath_variable_boolean*>(rhs)->value);\r\n\r\n\t\tdefault:\r\n\t\t\tassert(false && \"Invalid variable type\"); // unreachable\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result)\r\n\t{\r\n\t\tsize_t length = static_cast<size_t>(end - begin);\r\n\t\tchar_t* scratch = buffer;\r\n\r\n\t\tif (length >= sizeof(buffer) / sizeof(buffer[0]))\r\n\t\t{\r\n\t\t\t// need to make dummy on-heap copy\r\n\t\t\tscratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));\r\n\t\t\tif (!scratch) return false;\r\n\t\t}\r\n\r\n\t\t// copy string to zero-terminated buffer and perform lookup\r\n\t\tmemcpy(scratch, begin, length * sizeof(char_t));\r\n\t\tscratch[length] = 0;\r\n\r\n\t\t*out_result = set->get(scratch);\r\n\r\n\t\t// free dummy buffer\r\n\t\tif (scratch != buffer) xml_memory::deallocate(scratch);\r\n\r\n\t\treturn true;\r\n\t}\r\nPUGI_IMPL_NS_END\r\n\r\n// Internal node set class\r\nPUGI_IMPL_NS_BEGIN\r\n\tPUGI_IMPL_FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end)\r\n\t{\r\n\t\tif (end - begin < 2)\r\n\t\t\treturn xpath_node_set::type_sorted;\r\n\r\n\t\tdocument_order_comparator cmp;\r\n\r\n\t\tbool first = cmp(begin[0], begin[1]);\r\n\r\n\t\tfor (const xpath_node* it = begin + 1; it + 1 < end; ++it)\r\n\t\t\tif (cmp(it[0], it[1]) != first)\r\n\t\t\t\treturn xpath_node_set::type_unsorted;\r\n\r\n\t\treturn first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev)\r\n\t{\r\n\t\txpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted;\r\n\r\n\t\tif (type == xpath_node_set::type_unsorted)\r\n\t\t{\r\n\t\t\txpath_node_set::type_t sorted = xpath_get_order(begin, end);\r\n\r\n\t\t\tif (sorted == xpath_node_set::type_unsorted)\r\n\t\t\t{\r\n\t\t\t\tsort(begin, end, document_order_comparator());\r\n\r\n\t\t\t\ttype = xpath_node_set::type_sorted;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\ttype = sorted;\r\n\t\t}\r\n\r\n\t\tif (type != order) reverse(begin, end);\r\n\r\n\t\treturn order;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type)\r\n\t{\r\n\t\tif (begin == end) return xpath_node();\r\n\r\n\t\tswitch (type)\r\n\t\t{\r\n\t\tcase xpath_node_set::type_sorted:\r\n\t\t\treturn *begin;\r\n\r\n\t\tcase xpath_node_set::type_sorted_reverse:\r\n\t\t\treturn *(end - 1);\r\n\r\n\t\tcase xpath_node_set::type_unsorted:\r\n\t\t\treturn *min_element(begin, end, document_order_comparator());\r\n\r\n\t\tdefault:\r\n\t\t\tassert(false && \"Invalid node set type\"); // unreachable\r\n\t\t\treturn xpath_node();\r\n\t\t}\r\n\t}\r\n\r\n\tclass xpath_node_set_raw\r\n\t{\r\n\t\txpath_node_set::type_t _type;\r\n\r\n\t\txpath_node* _begin;\r\n\t\txpath_node* _end;\r\n\t\txpath_node* _eos;\r\n\r\n\tpublic:\r\n\t\txpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\txpath_node* begin() const\r\n\t\t{\r\n\t\t\treturn _begin;\r\n\t\t}\r\n\r\n\t\txpath_node* end() const\r\n\t\t{\r\n\t\t\treturn _end;\r\n\t\t}\r\n\r\n\t\tbool empty() const\r\n\t\t{\r\n\t\t\treturn _begin == _end;\r\n\t\t}\r\n\r\n\t\tsize_t size() const\r\n\t\t{\r\n\t\t\treturn static_cast<size_t>(_end - _begin);\r\n\t\t}\r\n\r\n\t\txpath_node first() const\r\n\t\t{\r\n\t\t\treturn xpath_first(_begin, _end, _type);\r\n\t\t}\r\n\r\n\t\tvoid push_back_grow(const xpath_node& node, xpath_allocator* alloc);\r\n\r\n\t\tvoid push_back(const xpath_node& node, xpath_allocator* alloc)\r\n\t\t{\r\n\t\t\tif (_end != _eos)\r\n\t\t\t\t*_end++ = node;\r\n\t\t\telse\r\n\t\t\t\tpush_back_grow(node, alloc);\r\n\t\t}\r\n\r\n\t\tvoid append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc)\r\n\t\t{\r\n\t\t\tif (begin_ == end_) return;\r\n\r\n\t\t\tsize_t size_ = static_cast<size_t>(_end - _begin);\r\n\t\t\tsize_t capacity = static_cast<size_t>(_eos - _begin);\r\n\t\t\tsize_t count = static_cast<size_t>(end_ - begin_);\r\n\r\n\t\t\tif (size_ + count > capacity)\r\n\t\t\t{\r\n\t\t\t\t// reallocate the old array or allocate a new one\r\n\t\t\t\txpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node)));\r\n\t\t\t\tif (!data) return;\r\n\r\n\t\t\t\t// finalize\r\n\t\t\t\t_begin = data;\r\n\t\t\t\t_end = data + size_;\r\n\t\t\t\t_eos = data + size_ + count;\r\n\t\t\t}\r\n\r\n\t\t\tmemcpy(_end, begin_, count * sizeof(xpath_node));\r\n\t\t\t_end += count;\r\n\t\t}\r\n\r\n\t\tvoid sort_do()\r\n\t\t{\r\n\t\t\t_type = xpath_sort(_begin, _end, _type, false);\r\n\t\t}\r\n\r\n\t\tvoid truncate(xpath_node* pos)\r\n\t\t{\r\n\t\t\tassert(_begin <= pos && pos <= _end);\r\n\r\n\t\t\t_end = pos;\r\n\t\t}\r\n\r\n\t\tvoid remove_duplicates(xpath_allocator* alloc)\r\n\t\t{\r\n\t\t\tif (_type == xpath_node_set::type_unsorted && _end - _begin > 2)\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(alloc);\r\n\r\n\t\t\t\tsize_t size_ = static_cast<size_t>(_end - _begin);\r\n\r\n\t\t\t\tsize_t hash_size = 1;\r\n\t\t\t\twhile (hash_size < size_ + size_ / 2) hash_size *= 2;\r\n\r\n\t\t\t\tconst void** hash_data = static_cast<const void**>(alloc->allocate(hash_size * sizeof(void**)));\r\n\t\t\t\tif (!hash_data) return;\r\n\r\n\t\t\t\tmemset(hash_data, 0, hash_size * sizeof(const void**));\r\n\r\n\t\t\t\txpath_node* write = _begin;\r\n\r\n\t\t\t\tfor (xpath_node* it = _begin; it != _end; ++it)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst void* attr = it->attribute().internal_object();\r\n\t\t\t\t\tconst void* node = it->node().internal_object();\r\n\t\t\t\t\tconst void* key = attr ? attr : node;\r\n\r\n\t\t\t\t\tif (key && hash_insert(hash_data, hash_size, key))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t*write++ = *it;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\t_end = write;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\t_end = unique(_begin, _end);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\txpath_node_set::type_t type() const\r\n\t\t{\r\n\t\t\treturn _type;\r\n\t\t}\r\n\r\n\t\tvoid set_type(xpath_node_set::type_t value)\r\n\t\t{\r\n\t\t\t_type = value;\r\n\t\t}\r\n\t};\r\n\r\n\tPUGI_IMPL_FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc)\r\n\t{\r\n\t\tsize_t capacity = static_cast<size_t>(_eos - _begin);\r\n\r\n\t\t// get new capacity (1.5x rule)\r\n\t\tsize_t new_capacity = capacity + capacity / 2 + 1;\r\n\r\n\t\t// reallocate the old array or allocate a new one\r\n\t\txpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node)));\r\n\t\tif (!data) return;\r\n\r\n\t\t// finalize\r\n\t\t_begin = data;\r\n\t\t_end = data + capacity;\r\n\t\t_eos = data + new_capacity;\r\n\r\n\t\t// push\r\n\t\t*_end++ = node;\r\n\t}\r\nPUGI_IMPL_NS_END\r\n\r\nPUGI_IMPL_NS_BEGIN\r\n\tstruct xpath_context\r\n\t{\r\n\t\txpath_node n;\r\n\t\tsize_t position, size;\r\n\r\n\t\txpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_)\r\n\t\t{\r\n\t\t}\r\n\t};\r\n\r\n\tenum lexeme_t\r\n\t{\r\n\t\tlex_none = 0,\r\n\t\tlex_equal,\r\n\t\tlex_not_equal,\r\n\t\tlex_less,\r\n\t\tlex_greater,\r\n\t\tlex_less_or_equal,\r\n\t\tlex_greater_or_equal,\r\n\t\tlex_plus,\r\n\t\tlex_minus,\r\n\t\tlex_multiply,\r\n\t\tlex_union,\r\n\t\tlex_var_ref,\r\n\t\tlex_open_brace,\r\n\t\tlex_close_brace,\r\n\t\tlex_quoted_string,\r\n\t\tlex_number,\r\n\t\tlex_slash,\r\n\t\tlex_double_slash,\r\n\t\tlex_open_square_brace,\r\n\t\tlex_close_square_brace,\r\n\t\tlex_string,\r\n\t\tlex_comma,\r\n\t\tlex_axis_attribute,\r\n\t\tlex_dot,\r\n\t\tlex_double_dot,\r\n\t\tlex_double_colon,\r\n\t\tlex_eof\r\n\t};\r\n\r\n\tstruct xpath_lexer_string\r\n\t{\r\n\t\tconst char_t* begin;\r\n\t\tconst char_t* end;\r\n\r\n\t\txpath_lexer_string(): begin(0), end(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tbool operator==(const char_t* other) const\r\n\t\t{\r\n\t\t\tsize_t length = static_cast<size_t>(end - begin);\r\n\r\n\t\t\treturn strequalrange(other, begin, length);\r\n\t\t}\r\n\t};\r\n\r\n\tclass xpath_lexer\r\n\t{\r\n\t\tconst char_t* _cur;\r\n\t\tconst char_t* _cur_lexeme_pos;\r\n\t\txpath_lexer_string _cur_lexeme_contents;\r\n\r\n\t\tlexeme_t _cur_lexeme;\r\n\r\n\tpublic:\r\n\t\texplicit xpath_lexer(const char_t* query): _cur(query)\r\n\t\t{\r\n\t\t\tnext();\r\n\t\t}\r\n\r\n\t\tconst char_t* state() const\r\n\t\t{\r\n\t\t\treturn _cur;\r\n\t\t}\r\n\r\n\t\tvoid next()\r\n\t\t{\r\n\t\t\tconst char_t* cur = _cur;\r\n\r\n\t\t\twhile (PUGI_IMPL_IS_CHARTYPE(*cur, ct_space)) ++cur;\r\n\r\n\t\t\t// save lexeme position for error reporting\r\n\t\t\t_cur_lexeme_pos = cur;\r\n\r\n\t\t\tswitch (*cur)\r\n\t\t\t{\r\n\t\t\tcase 0:\r\n\t\t\t\t_cur_lexeme = lex_eof;\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '>':\r\n\t\t\t\tif (*(cur+1) == '=')\r\n\t\t\t\t{\r\n\t\t\t\t\tcur += 2;\r\n\t\t\t\t\t_cur_lexeme = lex_greater_or_equal;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tcur += 1;\r\n\t\t\t\t\t_cur_lexeme = lex_greater;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '<':\r\n\t\t\t\tif (*(cur+1) == '=')\r\n\t\t\t\t{\r\n\t\t\t\t\tcur += 2;\r\n\t\t\t\t\t_cur_lexeme = lex_less_or_equal;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tcur += 1;\r\n\t\t\t\t\t_cur_lexeme = lex_less;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '!':\r\n\t\t\t\tif (*(cur+1) == '=')\r\n\t\t\t\t{\r\n\t\t\t\t\tcur += 2;\r\n\t\t\t\t\t_cur_lexeme = lex_not_equal;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t_cur_lexeme = lex_none;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '=':\r\n\t\t\t\tcur += 1;\r\n\t\t\t\t_cur_lexeme = lex_equal;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '+':\r\n\t\t\t\tcur += 1;\r\n\t\t\t\t_cur_lexeme = lex_plus;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '-':\r\n\t\t\t\tcur += 1;\r\n\t\t\t\t_cur_lexeme = lex_minus;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '*':\r\n\t\t\t\tcur += 1;\r\n\t\t\t\t_cur_lexeme = lex_multiply;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '|':\r\n\t\t\t\tcur += 1;\r\n\t\t\t\t_cur_lexeme = lex_union;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '$':\r\n\t\t\t\tcur += 1;\r\n\r\n\t\t\t\tif (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_start_symbol))\r\n\t\t\t\t{\r\n\t\t\t\t\t_cur_lexeme_contents.begin = cur;\r\n\r\n\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++;\r\n\r\n\t\t\t\t\tif (cur[0] == ':' && PUGI_IMPL_IS_CHARTYPEX(cur[1], ctx_symbol)) // qname\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcur++; // :\r\n\r\n\t\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t_cur_lexeme_contents.end = cur;\r\n\r\n\t\t\t\t\t_cur_lexeme = lex_var_ref;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t_cur_lexeme = lex_none;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '(':\r\n\t\t\t\tcur += 1;\r\n\t\t\t\t_cur_lexeme = lex_open_brace;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase ')':\r\n\t\t\t\tcur += 1;\r\n\t\t\t\t_cur_lexeme = lex_close_brace;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '[':\r\n\t\t\t\tcur += 1;\r\n\t\t\t\t_cur_lexeme = lex_open_square_brace;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase ']':\r\n\t\t\t\tcur += 1;\r\n\t\t\t\t_cur_lexeme = lex_close_square_brace;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase ',':\r\n\t\t\t\tcur += 1;\r\n\t\t\t\t_cur_lexeme = lex_comma;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '/':\r\n\t\t\t\tif (*(cur+1) == '/')\r\n\t\t\t\t{\r\n\t\t\t\t\tcur += 2;\r\n\t\t\t\t\t_cur_lexeme = lex_double_slash;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tcur += 1;\r\n\t\t\t\t\t_cur_lexeme = lex_slash;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '.':\r\n\t\t\t\tif (*(cur+1) == '.')\r\n\t\t\t\t{\r\n\t\t\t\t\tcur += 2;\r\n\t\t\t\t\t_cur_lexeme = lex_double_dot;\r\n\t\t\t\t}\r\n\t\t\t\telse if (PUGI_IMPL_IS_CHARTYPEX(*(cur+1), ctx_digit))\r\n\t\t\t\t{\r\n\t\t\t\t\t_cur_lexeme_contents.begin = cur; // .\r\n\r\n\t\t\t\t\t++cur;\r\n\r\n\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++;\r\n\r\n\t\t\t\t\t_cur_lexeme_contents.end = cur;\r\n\r\n\t\t\t\t\t_cur_lexeme = lex_number;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tcur += 1;\r\n\t\t\t\t\t_cur_lexeme = lex_dot;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '@':\r\n\t\t\t\tcur += 1;\r\n\t\t\t\t_cur_lexeme = lex_axis_attribute;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase '\"':\r\n\t\t\tcase '\\'':\r\n\t\t\t{\r\n\t\t\t\tchar_t terminator = *cur;\r\n\r\n\t\t\t\t++cur;\r\n\r\n\t\t\t\t_cur_lexeme_contents.begin = cur;\r\n\t\t\t\twhile (*cur && *cur != terminator) cur++;\r\n\t\t\t\t_cur_lexeme_contents.end = cur;\r\n\r\n\t\t\t\tif (!*cur)\r\n\t\t\t\t\t_cur_lexeme = lex_none;\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tcur += 1;\r\n\t\t\t\t\t_cur_lexeme = lex_quoted_string;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase ':':\r\n\t\t\t\tif (*(cur+1) == ':')\r\n\t\t\t\t{\r\n\t\t\t\t\tcur += 2;\r\n\t\t\t\t\t_cur_lexeme = lex_double_colon;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t_cur_lexeme = lex_none;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tif (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit))\r\n\t\t\t\t{\r\n\t\t\t\t\t_cur_lexeme_contents.begin = cur;\r\n\r\n\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++;\r\n\r\n\t\t\t\t\tif (*cur == '.')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcur++;\r\n\r\n\t\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t_cur_lexeme_contents.end = cur;\r\n\r\n\t\t\t\t\t_cur_lexeme = lex_number;\r\n\t\t\t\t}\r\n\t\t\t\telse if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_start_symbol))\r\n\t\t\t\t{\r\n\t\t\t\t\t_cur_lexeme_contents.begin = cur;\r\n\r\n\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++;\r\n\r\n\t\t\t\t\tif (cur[0] == ':')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (cur[1] == '*') // namespace test ncname:*\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcur += 2; // :*\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (PUGI_IMPL_IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcur++; // :\r\n\r\n\t\t\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t_cur_lexeme_contents.end = cur;\r\n\r\n\t\t\t\t\t_cur_lexeme = lex_string;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t_cur_lexeme = lex_none;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t_cur = cur;\r\n\t\t}\r\n\r\n\t\tlexeme_t current() const\r\n\t\t{\r\n\t\t\treturn _cur_lexeme;\r\n\t\t}\r\n\r\n\t\tconst char_t* current_pos() const\r\n\t\t{\r\n\t\t\treturn _cur_lexeme_pos;\r\n\t\t}\r\n\r\n\t\tconst xpath_lexer_string& contents() const\r\n\t\t{\r\n\t\t\tassert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string);\r\n\r\n\t\t\treturn _cur_lexeme_contents;\r\n\t\t}\r\n\t};\r\n\r\n\tenum ast_type_t\r\n\t{\r\n\t\tast_unknown,\r\n\t\tast_op_or,\t\t\t\t\t\t// left or right\r\n\t\tast_op_and,\t\t\t\t\t\t// left and right\r\n\t\tast_op_equal,\t\t\t\t\t// left = right\r\n\t\tast_op_not_equal,\t\t\t\t// left != right\r\n\t\tast_op_less,\t\t\t\t\t// left < right\r\n\t\tast_op_greater,\t\t\t\t\t// left > right\r\n\t\tast_op_less_or_equal,\t\t\t// left <= right\r\n\t\tast_op_greater_or_equal,\t\t// left >= right\r\n\t\tast_op_add,\t\t\t\t\t\t// left + right\r\n\t\tast_op_subtract,\t\t\t\t// left - right\r\n\t\tast_op_multiply,\t\t\t\t// left * right\r\n\t\tast_op_divide,\t\t\t\t\t// left / right\r\n\t\tast_op_mod,\t\t\t\t\t\t// left % right\r\n\t\tast_op_negate,\t\t\t\t\t// left - right\r\n\t\tast_op_union,\t\t\t\t\t// left | right\r\n\t\tast_predicate,\t\t\t\t\t// apply predicate to set; next points to next predicate\r\n\t\tast_filter,\t\t\t\t\t\t// select * from left where right\r\n\t\tast_string_constant,\t\t\t// string constant\r\n\t\tast_number_constant,\t\t\t// number constant\r\n\t\tast_variable,\t\t\t\t\t// variable\r\n\t\tast_func_last,\t\t\t\t\t// last()\r\n\t\tast_func_position,\t\t\t\t// position()\r\n\t\tast_func_count,\t\t\t\t\t// count(left)\r\n\t\tast_func_id,\t\t\t\t\t// id(left)\r\n\t\tast_func_local_name_0,\t\t\t// local-name()\r\n\t\tast_func_local_name_1,\t\t\t// local-name(left)\r\n\t\tast_func_namespace_uri_0,\t\t// namespace-uri()\r\n\t\tast_func_namespace_uri_1,\t\t// namespace-uri(left)\r\n\t\tast_func_name_0,\t\t\t\t// name()\r\n\t\tast_func_name_1,\t\t\t\t// name(left)\r\n\t\tast_func_string_0,\t\t\t\t// string()\r\n\t\tast_func_string_1,\t\t\t\t// string(left)\r\n\t\tast_func_concat,\t\t\t\t// concat(left, right, siblings)\r\n\t\tast_func_starts_with,\t\t\t// starts_with(left, right)\r\n\t\tast_func_contains,\t\t\t\t// contains(left, right)\r\n\t\tast_func_substring_before,\t\t// substring-before(left, right)\r\n\t\tast_func_substring_after,\t\t// substring-after(left, right)\r\n\t\tast_func_substring_2,\t\t\t// substring(left, right)\r\n\t\tast_func_substring_3,\t\t\t// substring(left, right, third)\r\n\t\tast_func_string_length_0,\t\t// string-length()\r\n\t\tast_func_string_length_1,\t\t// string-length(left)\r\n\t\tast_func_normalize_space_0,\t\t// normalize-space()\r\n\t\tast_func_normalize_space_1,\t\t// normalize-space(left)\r\n\t\tast_func_translate,\t\t\t\t// translate(left, right, third)\r\n\t\tast_func_boolean,\t\t\t\t// boolean(left)\r\n\t\tast_func_not,\t\t\t\t\t// not(left)\r\n\t\tast_func_true,\t\t\t\t\t// true()\r\n\t\tast_func_false,\t\t\t\t\t// false()\r\n\t\tast_func_lang,\t\t\t\t\t// lang(left)\r\n\t\tast_func_number_0,\t\t\t\t// number()\r\n\t\tast_func_number_1,\t\t\t\t// number(left)\r\n\t\tast_func_sum,\t\t\t\t\t// sum(left)\r\n\t\tast_func_floor,\t\t\t\t\t// floor(left)\r\n\t\tast_func_ceiling,\t\t\t\t// ceiling(left)\r\n\t\tast_func_round,\t\t\t\t\t// round(left)\r\n\t\tast_step,\t\t\t\t\t\t// process set left with step\r\n\t\tast_step_root,\t\t\t\t\t// select root node\r\n\r\n\t\tast_opt_translate_table,\t\t// translate(left, right, third) where right/third are constants\r\n\t\tast_opt_compare_attribute\t\t// @name = 'string'\r\n\t};\r\n\r\n\tenum axis_t\r\n\t{\r\n\t\taxis_ancestor,\r\n\t\taxis_ancestor_or_self,\r\n\t\taxis_attribute,\r\n\t\taxis_child,\r\n\t\taxis_descendant,\r\n\t\taxis_descendant_or_self,\r\n\t\taxis_following,\r\n\t\taxis_following_sibling,\r\n\t\taxis_namespace,\r\n\t\taxis_parent,\r\n\t\taxis_preceding,\r\n\t\taxis_preceding_sibling,\r\n\t\taxis_self\r\n\t};\r\n\r\n\tenum nodetest_t\r\n\t{\r\n\t\tnodetest_none,\r\n\t\tnodetest_name,\r\n\t\tnodetest_type_node,\r\n\t\tnodetest_type_comment,\r\n\t\tnodetest_type_pi,\r\n\t\tnodetest_type_text,\r\n\t\tnodetest_pi,\r\n\t\tnodetest_all,\r\n\t\tnodetest_all_in_namespace\r\n\t};\r\n\r\n\tenum predicate_t\r\n\t{\r\n\t\tpredicate_default,\r\n\t\tpredicate_posinv,\r\n\t\tpredicate_constant,\r\n\t\tpredicate_constant_one\r\n\t};\r\n\r\n\tenum nodeset_eval_t\r\n\t{\r\n\t\tnodeset_eval_all,\r\n\t\tnodeset_eval_any,\r\n\t\tnodeset_eval_first\r\n\t};\r\n\r\n\ttemplate <axis_t N> struct axis_to_type\r\n\t{\r\n\t\tstatic const axis_t axis;\r\n\t};\r\n\r\n\ttemplate <axis_t N> const axis_t axis_to_type<N>::axis = N;\r\n\r\n\tclass xpath_ast_node\r\n\t{\r\n\tprivate:\r\n\t\t// node type\r\n\t\tchar _type;\r\n\t\tchar _rettype;\r\n\r\n\t\t// for ast_step\r\n\t\tchar _axis;\r\n\r\n\t\t// for ast_step/ast_predicate/ast_filter\r\n\t\tchar _test;\r\n\r\n\t\t// tree node structure\r\n\t\txpath_ast_node* _left;\r\n\t\txpath_ast_node* _right;\r\n\t\txpath_ast_node* _next;\r\n\r\n\t\tunion\r\n\t\t{\r\n\t\t\t// value for ast_string_constant\r\n\t\t\tconst char_t* string;\r\n\t\t\t// value for ast_number_constant\r\n\t\t\tdouble number;\r\n\t\t\t// variable for ast_variable\r\n\t\t\txpath_variable* variable;\r\n\t\t\t// node test for ast_step (node name/namespace/node type/pi target)\r\n\t\t\tconst char_t* nodetest;\r\n\t\t\t// table for ast_opt_translate_table\r\n\t\t\tconst unsigned char* table;\r\n\t\t} _data;\r\n\r\n\t\txpath_ast_node(const xpath_ast_node&);\r\n\t\txpath_ast_node& operator=(const xpath_ast_node&);\r\n\r\n\t\ttemplate <class Comp> static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp)\r\n\t\t{\r\n\t\t\txpath_value_type lt = lhs->rettype(), rt = rhs->rettype();\r\n\r\n\t\t\tif (lt != xpath_type_node_set && rt != xpath_type_node_set)\r\n\t\t\t{\r\n\t\t\t\tif (lt == xpath_type_boolean || rt == xpath_type_boolean)\r\n\t\t\t\t\treturn comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack));\r\n\t\t\t\telse if (lt == xpath_type_number || rt == xpath_type_number)\r\n\t\t\t\t\treturn comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack));\r\n\t\t\t\telse if (lt == xpath_type_string || rt == xpath_type_string)\r\n\t\t\t\t{\r\n\t\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\t\txpath_string ls = lhs->eval_string(c, stack);\r\n\t\t\t\t\txpath_string rs = rhs->eval_string(c, stack);\r\n\r\n\t\t\t\t\treturn comp(ls, rs);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (lt == xpath_type_node_set && rt == xpath_type_node_set)\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\txpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);\r\n\t\t\t\txpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);\r\n\r\n\t\t\t\tfor (const xpath_node* li = ls.begin(); li != ls.end(); ++li)\r\n\t\t\t\t\tfor (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\txpath_allocator_capture cri(stack.result);\r\n\r\n\t\t\t\t\t\tif (comp(string_value(*li, stack.result), string_value(*ri, stack.result)))\r\n\t\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tif (lt == xpath_type_node_set)\r\n\t\t\t\t{\r\n\t\t\t\t\tswap(lhs, rhs);\r\n\t\t\t\t\tswap(lt, rt);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (lt == xpath_type_boolean)\r\n\t\t\t\t\treturn comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack));\r\n\t\t\t\telse if (lt == xpath_type_number)\r\n\t\t\t\t{\r\n\t\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\t\tdouble l = lhs->eval_number(c, stack);\r\n\t\t\t\t\txpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);\r\n\r\n\t\t\t\t\tfor (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\txpath_allocator_capture cri(stack.result);\r\n\r\n\t\t\t\t\t\tif (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))\r\n\t\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t\telse if (lt == xpath_type_string)\r\n\t\t\t\t{\r\n\t\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\t\txpath_string l = lhs->eval_string(c, stack);\r\n\t\t\t\t\txpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);\r\n\r\n\t\t\t\t\tfor (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\txpath_allocator_capture cri(stack.result);\r\n\r\n\t\t\t\t\t\tif (comp(l, string_value(*ri, stack.result)))\r\n\t\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tassert(false && \"Wrong types\"); // unreachable\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tstatic bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval)\r\n\t\t{\r\n\t\t\treturn type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any;\r\n\t\t}\r\n\r\n\t\ttemplate <class Comp> static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp)\r\n\t\t{\r\n\t\t\txpath_value_type lt = lhs->rettype(), rt = rhs->rettype();\r\n\r\n\t\t\tif (lt != xpath_type_node_set && rt != xpath_type_node_set)\r\n\t\t\t\treturn comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack));\r\n\t\t\telse if (lt == xpath_type_node_set && rt == xpath_type_node_set)\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\txpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);\r\n\t\t\t\txpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);\r\n\r\n\t\t\t\tfor (const xpath_node* li = ls.begin(); li != ls.end(); ++li)\r\n\t\t\t\t{\r\n\t\t\t\t\txpath_allocator_capture cri(stack.result);\r\n\r\n\t\t\t\t\tdouble l = convert_string_to_number(string_value(*li, stack.result).c_str());\r\n\r\n\t\t\t\t\tfor (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\txpath_allocator_capture crii(stack.result);\r\n\r\n\t\t\t\t\t\tif (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))\r\n\t\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\telse if (lt != xpath_type_node_set && rt == xpath_type_node_set)\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\tdouble l = lhs->eval_number(c, stack);\r\n\t\t\t\txpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);\r\n\r\n\t\t\t\tfor (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)\r\n\t\t\t\t{\r\n\t\t\t\t\txpath_allocator_capture cri(stack.result);\r\n\r\n\t\t\t\t\tif (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))\r\n\t\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\telse if (lt == xpath_type_node_set && rt != xpath_type_node_set)\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\txpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);\r\n\t\t\t\tdouble r = rhs->eval_number(c, stack);\r\n\r\n\t\t\t\tfor (const xpath_node* li = ls.begin(); li != ls.end(); ++li)\r\n\t\t\t\t{\r\n\t\t\t\t\txpath_allocator_capture cri(stack.result);\r\n\r\n\t\t\t\t\tif (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r))\r\n\t\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tassert(false && \"Wrong types\"); // unreachable\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tstatic void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once)\r\n\t\t{\r\n\t\t\tassert(ns.size() >= first);\r\n\t\t\tassert(expr->rettype() != xpath_type_number);\r\n\r\n\t\t\tsize_t i = 1;\r\n\t\t\tsize_t size = ns.size() - first;\r\n\r\n\t\t\txpath_node* last = ns.begin() + first;\r\n\r\n\t\t\t// remove_if... or well, sort of\r\n\t\t\tfor (xpath_node* it = last; it != ns.end(); ++it, ++i)\r\n\t\t\t{\r\n\t\t\t\txpath_context c(*it, i, size);\r\n\r\n\t\t\t\tif (expr->eval_boolean(c, stack))\r\n\t\t\t\t{\r\n\t\t\t\t\t*last++ = *it;\r\n\r\n\t\t\t\t\tif (once) break;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tns.truncate(last);\r\n\t\t}\r\n\r\n\t\tstatic void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once)\r\n\t\t{\r\n\t\t\tassert(ns.size() >= first);\r\n\t\t\tassert(expr->rettype() == xpath_type_number);\r\n\r\n\t\t\tsize_t i = 1;\r\n\t\t\tsize_t size = ns.size() - first;\r\n\r\n\t\t\txpath_node* last = ns.begin() + first;\r\n\r\n\t\t\t// remove_if... or well, sort of\r\n\t\t\tfor (xpath_node* it = last; it != ns.end(); ++it, ++i)\r\n\t\t\t{\r\n\t\t\t\txpath_context c(*it, i, size);\r\n\r\n\t\t\t\tif (expr->eval_number(c, stack) == static_cast<double>(i))\r\n\t\t\t\t{\r\n\t\t\t\t\t*last++ = *it;\r\n\r\n\t\t\t\t\tif (once) break;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tns.truncate(last);\r\n\t\t}\r\n\r\n\t\tstatic void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)\r\n\t\t{\r\n\t\t\tassert(ns.size() >= first);\r\n\t\t\tassert(expr->rettype() == xpath_type_number);\r\n\r\n\t\t\tsize_t size = ns.size() - first;\r\n\r\n\t\t\txpath_node* last = ns.begin() + first;\r\n\r\n\t\t\txpath_node cn;\r\n\t\t\txpath_context c(cn, 1, size);\r\n\r\n\t\t\tdouble er = expr->eval_number(c, stack);\r\n\r\n\t\t\tif (er >= 1.0 && er <= static_cast<double>(size))\r\n\t\t\t{\r\n\t\t\t\tsize_t eri = static_cast<size_t>(er);\r\n\r\n\t\t\t\tif (er == static_cast<double>(eri))\r\n\t\t\t\t{\r\n\t\t\t\t\txpath_node r = last[eri - 1];\r\n\r\n\t\t\t\t\t*last++ = r;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tns.truncate(last);\r\n\t\t}\r\n\r\n\t\tvoid apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once)\r\n\t\t{\r\n\t\t\tif (ns.size() == first) return;\r\n\r\n\t\t\tassert(_type == ast_filter || _type == ast_predicate);\r\n\r\n\t\t\tif (_test == predicate_constant || _test == predicate_constant_one)\r\n\t\t\t\tapply_predicate_number_const(ns, first, _right, stack);\r\n\t\t\telse if (_right->rettype() == xpath_type_number)\r\n\t\t\t\tapply_predicate_number(ns, first, _right, stack, once);\r\n\t\t\telse\r\n\t\t\t\tapply_predicate_boolean(ns, first, _right, stack, once);\r\n\t\t}\r\n\r\n\t\tvoid apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval)\r\n\t\t{\r\n\t\t\tif (ns.size() == first) return;\r\n\r\n\t\t\tbool last_once = eval_once(ns.type(), eval);\r\n\r\n\t\t\tfor (xpath_ast_node* pred = _right; pred; pred = pred->_next)\r\n\t\t\t\tpred->apply_predicate(ns, first, stack, !pred->_next && last_once);\r\n\t\t}\r\n\r\n\t\tbool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc)\r\n\t\t{\r\n\t\t\tassert(a);\r\n\r\n\t\t\tconst char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT(\"\");\r\n\r\n\t\t\tswitch (_test)\r\n\t\t\t{\r\n\t\t\tcase nodetest_name:\r\n\t\t\t\tif (strequal(name, _data.nodetest) && is_xpath_attribute(name))\r\n\t\t\t\t{\r\n\t\t\t\t\tns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase nodetest_type_node:\r\n\t\t\tcase nodetest_all:\r\n\t\t\t\tif (is_xpath_attribute(name))\r\n\t\t\t\t{\r\n\t\t\t\t\tns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase nodetest_all_in_namespace:\r\n\t\t\t\tif (starts_with(name, _data.nodetest) && is_xpath_attribute(name))\r\n\t\t\t\t{\r\n\t\t\t\t\tns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tdefault:\r\n\t\t\t\t;\r\n\t\t\t}\r\n\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\tbool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc)\r\n\t\t{\r\n\t\t\tassert(n);\r\n\r\n\t\t\txml_node_type type = PUGI_IMPL_NODETYPE(n);\r\n\r\n\t\t\tswitch (_test)\r\n\t\t\t{\r\n\t\t\tcase nodetest_name:\r\n\t\t\t\tif (type == node_element && n->name && strequal(n->name, _data.nodetest))\r\n\t\t\t\t{\r\n\t\t\t\t\tns.push_back(xml_node(n), alloc);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase nodetest_type_node:\r\n\t\t\t\tns.push_back(xml_node(n), alloc);\r\n\t\t\t\treturn true;\r\n\r\n\t\t\tcase nodetest_type_comment:\r\n\t\t\t\tif (type == node_comment)\r\n\t\t\t\t{\r\n\t\t\t\t\tns.push_back(xml_node(n), alloc);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase nodetest_type_text:\r\n\t\t\t\tif (type == node_pcdata || type == node_cdata)\r\n\t\t\t\t{\r\n\t\t\t\t\tns.push_back(xml_node(n), alloc);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase nodetest_type_pi:\r\n\t\t\t\tif (type == node_pi)\r\n\t\t\t\t{\r\n\t\t\t\t\tns.push_back(xml_node(n), alloc);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase nodetest_pi:\r\n\t\t\t\tif (type == node_pi && n->name && strequal(n->name, _data.nodetest))\r\n\t\t\t\t{\r\n\t\t\t\t\tns.push_back(xml_node(n), alloc);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase nodetest_all:\r\n\t\t\t\tif (type == node_element)\r\n\t\t\t\t{\r\n\t\t\t\t\tns.push_back(xml_node(n), alloc);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase nodetest_all_in_namespace:\r\n\t\t\t\tif (type == node_element && n->name && starts_with(n->name, _data.nodetest))\r\n\t\t\t\t{\r\n\t\t\t\t\tns.push_back(xml_node(n), alloc);\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tassert(false && \"Unknown axis\"); // unreachable\r\n\t\t\t}\r\n\r\n\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\ttemplate <class T> void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T)\r\n\t\t{\r\n\t\t\tconst axis_t axis = T::axis;\r\n\r\n\t\t\tswitch (axis)\r\n\t\t\t{\r\n\t\t\tcase axis_attribute:\r\n\t\t\t{\r\n\t\t\t\tfor (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute)\r\n\t\t\t\t\tif (step_push(ns, a, n, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_child:\r\n\t\t\t{\r\n\t\t\t\tfor (xml_node_struct* c = n->first_child; c; c = c->next_sibling)\r\n\t\t\t\t\tif (step_push(ns, c, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_descendant:\r\n\t\t\tcase axis_descendant_or_self:\r\n\t\t\t{\r\n\t\t\t\tif (axis == axis_descendant_or_self)\r\n\t\t\t\t\tif (step_push(ns, n, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\txml_node_struct* cur = n->first_child;\r\n\r\n\t\t\t\twhile (cur)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (step_push(ns, cur, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\tif (cur->first_child)\r\n\t\t\t\t\t\tcur = cur->first_child;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\twhile (!cur->next_sibling)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcur = cur->parent;\r\n\r\n\t\t\t\t\t\t\tif (cur == n) return;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tcur = cur->next_sibling;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_following_sibling:\r\n\t\t\t{\r\n\t\t\t\tfor (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling)\r\n\t\t\t\t\tif (step_push(ns, c, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_preceding_sibling:\r\n\t\t\t{\r\n\t\t\t\tfor (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c)\r\n\t\t\t\t\tif (step_push(ns, c, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_following:\r\n\t\t\t{\r\n\t\t\t\txml_node_struct* cur = n;\r\n\r\n\t\t\t\t// exit from this node so that we don't include descendants\r\n\t\t\t\twhile (!cur->next_sibling)\r\n\t\t\t\t{\r\n\t\t\t\t\tcur = cur->parent;\r\n\r\n\t\t\t\t\tif (!cur) return;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcur = cur->next_sibling;\r\n\r\n\t\t\t\twhile (cur)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (step_push(ns, cur, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\tif (cur->first_child)\r\n\t\t\t\t\t\tcur = cur->first_child;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\twhile (!cur->next_sibling)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcur = cur->parent;\r\n\r\n\t\t\t\t\t\t\tif (!cur) return;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tcur = cur->next_sibling;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_preceding:\r\n\t\t\t{\r\n\t\t\t\txml_node_struct* cur = n;\r\n\r\n\t\t\t\t// exit from this node so that we don't include descendants\r\n\t\t\t\twhile (!cur->prev_sibling_c->next_sibling)\r\n\t\t\t\t{\r\n\t\t\t\t\tcur = cur->parent;\r\n\r\n\t\t\t\t\tif (!cur) return;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tcur = cur->prev_sibling_c;\r\n\r\n\t\t\t\twhile (cur)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (cur->first_child)\r\n\t\t\t\t\t\tcur = cur->first_child->prev_sibling_c;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// leaf node, can't be ancestor\r\n\t\t\t\t\t\tif (step_push(ns, cur, alloc) & once)\r\n\t\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\t\twhile (!cur->prev_sibling_c->next_sibling)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcur = cur->parent;\r\n\r\n\t\t\t\t\t\t\tif (!cur) return;\r\n\r\n\t\t\t\t\t\t\tif (!node_is_ancestor(cur, n))\r\n\t\t\t\t\t\t\t\tif (step_push(ns, cur, alloc) & once)\r\n\t\t\t\t\t\t\t\t\treturn;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tcur = cur->prev_sibling_c;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_ancestor:\r\n\t\t\tcase axis_ancestor_or_self:\r\n\t\t\t{\r\n\t\t\t\tif (axis == axis_ancestor_or_self)\r\n\t\t\t\t\tif (step_push(ns, n, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\txml_node_struct* cur = n->parent;\r\n\r\n\t\t\t\twhile (cur)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (step_push(ns, cur, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\tcur = cur->parent;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_self:\r\n\t\t\t{\r\n\t\t\t\tstep_push(ns, n, alloc);\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_parent:\r\n\t\t\t{\r\n\t\t\t\tif (n->parent)\r\n\t\t\t\t\tstep_push(ns, n->parent, alloc);\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tassert(false && \"Unimplemented axis\"); // unreachable\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\ttemplate <class T> void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v)\r\n\t\t{\r\n\t\t\tconst axis_t axis = T::axis;\r\n\r\n\t\t\tswitch (axis)\r\n\t\t\t{\r\n\t\t\tcase axis_ancestor:\r\n\t\t\tcase axis_ancestor_or_self:\r\n\t\t\t{\r\n\t\t\t\tif (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test\r\n\t\t\t\t\tif (step_push(ns, a, p, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\txml_node_struct* cur = p;\r\n\r\n\t\t\t\twhile (cur)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (step_push(ns, cur, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\r\n\t\t\t\t\tcur = cur->parent;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_descendant_or_self:\r\n\t\t\tcase axis_self:\r\n\t\t\t{\r\n\t\t\t\tif (_test == nodetest_type_node) // reject attributes based on principal node type test\r\n\t\t\t\t\tstep_push(ns, a, p, alloc);\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_following:\r\n\t\t\t{\r\n\t\t\t\txml_node_struct* cur = p;\r\n\r\n\t\t\t\twhile (cur)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (cur->first_child)\r\n\t\t\t\t\t\tcur = cur->first_child;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\twhile (!cur->next_sibling)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tcur = cur->parent;\r\n\r\n\t\t\t\t\t\t\tif (!cur) return;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tcur = cur->next_sibling;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (step_push(ns, cur, alloc) & once)\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_parent:\r\n\t\t\t{\r\n\t\t\t\tstep_push(ns, p, alloc);\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tcase axis_preceding:\r\n\t\t\t{\r\n\t\t\t\t// preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding\r\n\t\t\t\tstep_fill(ns, p, alloc, once, v);\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tassert(false && \"Unimplemented axis\"); // unreachable\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\ttemplate <class T> void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v)\r\n\t\t{\r\n\t\t\tconst axis_t axis = T::axis;\r\n\t\t\tconst bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self);\r\n\r\n\t\t\tif (xn.node())\r\n\t\t\t\tstep_fill(ns, xn.node().internal_object(), alloc, once, v);\r\n\t\t\telse if (axis_has_attributes && xn.attribute() && xn.parent())\r\n\t\t\t\tstep_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v);\r\n\t\t}\r\n\r\n\t\ttemplate <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v)\r\n\t\t{\r\n\t\t\tconst axis_t axis = T::axis;\r\n\t\t\tconst bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling);\r\n\t\t\tconst xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted;\r\n\r\n\t\t\tbool once =\r\n\t\t\t\t(axis == axis_attribute && _test == nodetest_name) ||\r\n\t\t\t\t(!_right && eval_once(axis_type, eval)) ||\r\n\t\t\t    // coverity[mixed_enums]\r\n\t\t\t\t(_right && !_right->_next && _right->_test == predicate_constant_one);\r\n\r\n\t\t\txpath_node_set_raw ns;\r\n\t\t\tns.set_type(axis_type);\r\n\r\n\t\t\tif (_left)\r\n\t\t\t{\r\n\t\t\t\txpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all);\r\n\r\n\t\t\t\t// self axis preserves the original order\r\n\t\t\t\tif (axis == axis_self) ns.set_type(s.type());\r\n\r\n\t\t\t\tfor (const xpath_node* it = s.begin(); it != s.end(); ++it)\r\n\t\t\t\t{\r\n\t\t\t\t\tsize_t size = ns.size();\r\n\r\n\t\t\t\t\t// in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes\r\n\t\t\t\t\tif (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted);\r\n\r\n\t\t\t\t\tstep_fill(ns, *it, stack.result, once, v);\r\n\t\t\t\t\tif (_right) apply_predicates(ns, size, stack, eval);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tstep_fill(ns, c.n, stack.result, once, v);\r\n\t\t\t\tif (_right) apply_predicates(ns, 0, stack, eval);\r\n\t\t\t}\r\n\r\n\t\t\t// child, attribute and self axes always generate unique set of nodes\r\n\t\t\t// for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice\r\n\t\t\tif (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted)\r\n\t\t\t\tns.remove_duplicates(stack.temp);\r\n\r\n\t\t\treturn ns;\r\n\t\t}\r\n\r\n\tpublic:\r\n\t\txpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value):\r\n\t\t\t_type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)\r\n\t\t{\r\n\t\t\tassert(type == ast_string_constant);\r\n\t\t\t_data.string = value;\r\n\t\t}\r\n\r\n\t\txpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value):\r\n\t\t\t_type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)\r\n\t\t{\r\n\t\t\tassert(type == ast_number_constant);\r\n\t\t\t_data.number = value;\r\n\t\t}\r\n\r\n\t\txpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value):\r\n\t\t\t_type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)\r\n\t\t{\r\n\t\t\tassert(type == ast_variable);\r\n\t\t\t_data.variable = value;\r\n\t\t}\r\n\r\n\t\txpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0):\r\n\t\t\t_type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\txpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents):\r\n\t\t\t_type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(static_cast<char>(axis)), _test(static_cast<char>(test)), _left(left), _right(0), _next(0)\r\n\t\t{\r\n\t\t\tassert(type == ast_step);\r\n\t\t\t_data.nodetest = contents;\r\n\t\t}\r\n\r\n\t\txpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test):\r\n\t\t\t_type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast<char>(test)), _left(left), _right(right), _next(0)\r\n\t\t{\r\n\t\t\tassert(type == ast_filter || type == ast_predicate);\r\n\t\t}\r\n\r\n\t\tvoid set_next(xpath_ast_node* value)\r\n\t\t{\r\n\t\t\t_next = value;\r\n\t\t}\r\n\r\n\t\tvoid set_right(xpath_ast_node* value)\r\n\t\t{\r\n\t\t\t_right = value;\r\n\t\t}\r\n\r\n\t\tbool eval_boolean(const xpath_context& c, const xpath_stack& stack)\r\n\t\t{\r\n\t\t\tswitch (_type)\r\n\t\t\t{\r\n\t\t\tcase ast_op_or:\r\n\t\t\t\treturn _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack);\r\n\r\n\t\t\tcase ast_op_and:\r\n\t\t\t\treturn _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack);\r\n\r\n\t\t\tcase ast_op_equal:\r\n\t\t\t\treturn compare_eq(_left, _right, c, stack, equal_to());\r\n\r\n\t\t\tcase ast_op_not_equal:\r\n\t\t\t\treturn compare_eq(_left, _right, c, stack, not_equal_to());\r\n\r\n\t\t\tcase ast_op_less:\r\n\t\t\t\treturn compare_rel(_left, _right, c, stack, less());\r\n\r\n\t\t\tcase ast_op_greater:\r\n\t\t\t\treturn compare_rel(_right, _left, c, stack, less());\r\n\r\n\t\t\tcase ast_op_less_or_equal:\r\n\t\t\t\treturn compare_rel(_left, _right, c, stack, less_equal());\r\n\r\n\t\t\tcase ast_op_greater_or_equal:\r\n\t\t\t\treturn compare_rel(_right, _left, c, stack, less_equal());\r\n\r\n\t\t\tcase ast_func_starts_with:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\txpath_string lr = _left->eval_string(c, stack);\r\n\t\t\t\txpath_string rr = _right->eval_string(c, stack);\r\n\r\n\t\t\t\treturn starts_with(lr.c_str(), rr.c_str());\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_contains:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\txpath_string lr = _left->eval_string(c, stack);\r\n\t\t\t\txpath_string rr = _right->eval_string(c, stack);\r\n\r\n\t\t\t\treturn find_substring(lr.c_str(), rr.c_str()) != 0;\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_boolean:\r\n\t\t\t\treturn _left->eval_boolean(c, stack);\r\n\r\n\t\t\tcase ast_func_not:\r\n\t\t\t\treturn !_left->eval_boolean(c, stack);\r\n\r\n\t\t\tcase ast_func_true:\r\n\t\t\t\treturn true;\r\n\r\n\t\t\tcase ast_func_false:\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tcase ast_func_lang:\r\n\t\t\t{\r\n\t\t\t\tif (c.n.attribute()) return false;\r\n\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\txpath_string lang = _left->eval_string(c, stack);\r\n\r\n\t\t\t\tfor (xml_node n = c.n.node(); n; n = n.parent())\r\n\t\t\t\t{\r\n\t\t\t\t\txml_attribute a = n.attribute(PUGIXML_TEXT(\"xml:lang\"));\r\n\r\n\t\t\t\t\tif (a)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tconst char_t* value = a.value();\r\n\r\n\t\t\t\t\t\t// strnicmp / strncasecmp is not portable\r\n\t\t\t\t\t\tfor (const char_t* lit = lang.c_str(); *lit; ++lit)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (tolower_ascii(*lit) != tolower_ascii(*value)) return false;\r\n\t\t\t\t\t\t\t++value;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\treturn *value == 0 || *value == '-';\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_opt_compare_attribute:\r\n\t\t\t{\r\n\t\t\t\tconst char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string();\r\n\r\n\t\t\t\txml_attribute attr = c.n.node().attribute(_left->_data.nodetest);\r\n\r\n\t\t\t\treturn attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name());\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_variable:\r\n\t\t\t{\r\n\t\t\t\tassert(_rettype == _data.variable->type());\r\n\r\n\t\t\t\tif (_rettype == xpath_type_boolean)\r\n\t\t\t\t\treturn _data.variable->get_boolean();\r\n\r\n\t\t\t\t// variable needs to be converted to the correct type, this is handled by the fallthrough block below\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\t;\r\n\t\t\t}\r\n\r\n\t\t\t// none of the ast types that return the value directly matched, we need to perform type conversion\r\n\t\t\tswitch (_rettype)\r\n\t\t\t{\r\n\t\t\tcase xpath_type_number:\r\n\t\t\t\treturn convert_number_to_boolean(eval_number(c, stack));\r\n\r\n\t\t\tcase xpath_type_string:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\treturn !eval_string(c, stack).empty();\r\n\t\t\t}\r\n\r\n\t\t\tcase xpath_type_node_set:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\treturn !eval_node_set(c, stack, nodeset_eval_any).empty();\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tassert(false && \"Wrong expression for return type boolean\"); // unreachable\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tdouble eval_number(const xpath_context& c, const xpath_stack& stack)\r\n\t\t{\r\n\t\t\tswitch (_type)\r\n\t\t\t{\r\n\t\t\tcase ast_op_add:\r\n\t\t\t\treturn _left->eval_number(c, stack) + _right->eval_number(c, stack);\r\n\r\n\t\t\tcase ast_op_subtract:\r\n\t\t\t\treturn _left->eval_number(c, stack) - _right->eval_number(c, stack);\r\n\r\n\t\t\tcase ast_op_multiply:\r\n\t\t\t\treturn _left->eval_number(c, stack) * _right->eval_number(c, stack);\r\n\r\n\t\t\tcase ast_op_divide:\r\n\t\t\t\treturn _left->eval_number(c, stack) / _right->eval_number(c, stack);\r\n\r\n\t\t\tcase ast_op_mod:\r\n\t\t\t\treturn fmod(_left->eval_number(c, stack), _right->eval_number(c, stack));\r\n\r\n\t\t\tcase ast_op_negate:\r\n\t\t\t\treturn -_left->eval_number(c, stack);\r\n\r\n\t\t\tcase ast_number_constant:\r\n\t\t\t\treturn _data.number;\r\n\r\n\t\t\tcase ast_func_last:\r\n\t\t\t\treturn static_cast<double>(c.size);\r\n\r\n\t\t\tcase ast_func_position:\r\n\t\t\t\treturn static_cast<double>(c.position);\r\n\r\n\t\t\tcase ast_func_count:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\treturn static_cast<double>(_left->eval_node_set(c, stack, nodeset_eval_all).size());\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_string_length_0:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\treturn static_cast<double>(string_value(c.n, stack.result).length());\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_string_length_1:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\treturn static_cast<double>(_left->eval_string(c, stack).length());\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_number_0:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\treturn convert_string_to_number(string_value(c.n, stack.result).c_str());\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_number_1:\r\n\t\t\t\treturn _left->eval_number(c, stack);\r\n\r\n\t\t\tcase ast_func_sum:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\tdouble r = 0;\r\n\r\n\t\t\t\txpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all);\r\n\r\n\t\t\t\tfor (const xpath_node* it = ns.begin(); it != ns.end(); ++it)\r\n\t\t\t\t{\r\n\t\t\t\t\txpath_allocator_capture cri(stack.result);\r\n\r\n\t\t\t\t\tr += convert_string_to_number(string_value(*it, stack.result).c_str());\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn r;\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_floor:\r\n\t\t\t{\r\n\t\t\t\tdouble r = _left->eval_number(c, stack);\r\n\r\n\t\t\t\treturn r == r ? floor(r) : r;\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_ceiling:\r\n\t\t\t{\r\n\t\t\t\tdouble r = _left->eval_number(c, stack);\r\n\r\n\t\t\t\treturn r == r ? ceil(r) : r;\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_round:\r\n\t\t\t\treturn round_nearest_nzero(_left->eval_number(c, stack));\r\n\r\n\t\t\tcase ast_variable:\r\n\t\t\t{\r\n\t\t\t\tassert(_rettype == _data.variable->type());\r\n\r\n\t\t\t\tif (_rettype == xpath_type_number)\r\n\t\t\t\t\treturn _data.variable->get_number();\r\n\r\n\t\t\t\t// variable needs to be converted to the correct type, this is handled by the fallthrough block below\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\t;\r\n\t\t\t}\r\n\r\n\t\t\t// none of the ast types that return the value directly matched, we need to perform type conversion\r\n\t\t\tswitch (_rettype)\r\n\t\t\t{\r\n\t\t\tcase xpath_type_boolean:\r\n\t\t\t\treturn eval_boolean(c, stack) ? 1 : 0;\r\n\r\n\t\t\tcase xpath_type_string:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\treturn convert_string_to_number(eval_string(c, stack).c_str());\r\n\t\t\t}\r\n\r\n\t\t\tcase xpath_type_node_set:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\treturn convert_string_to_number(eval_string(c, stack).c_str());\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tassert(false && \"Wrong expression for return type number\"); // unreachable\r\n\t\t\t\treturn 0;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\txpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack)\r\n\t\t{\r\n\t\t\tassert(_type == ast_func_concat);\r\n\r\n\t\t\txpath_allocator_capture ct(stack.temp);\r\n\r\n\t\t\t// count the string number\r\n\t\t\tsize_t count = 1;\r\n\t\t\tfor (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++;\r\n\r\n\t\t\t// allocate a buffer for temporary string objects\r\n\t\t\txpath_string* buffer = static_cast<xpath_string*>(stack.temp->allocate(count * sizeof(xpath_string)));\r\n\t\t\tif (!buffer) return xpath_string();\r\n\r\n\t\t\t// evaluate all strings to temporary stack\r\n\t\t\txpath_stack swapped_stack = {stack.temp, stack.result};\r\n\r\n\t\t\tbuffer[0] = _left->eval_string(c, swapped_stack);\r\n\r\n\t\t\tsize_t pos = 1;\r\n\t\t\tfor (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack);\r\n\t\t\tassert(pos == count);\r\n\r\n\t\t\t// get total length\r\n\t\t\tsize_t length = 0;\r\n\t\t\tfor (size_t i = 0; i < count; ++i) length += buffer[i].length();\r\n\r\n\t\t\t// create final string\r\n\t\t\tchar_t* result = static_cast<char_t*>(stack.result->allocate((length + 1) * sizeof(char_t)));\r\n\t\t\tif (!result) return xpath_string();\r\n\r\n\t\t\tchar_t* ri = result;\r\n\r\n\t\t\tfor (size_t j = 0; j < count; ++j)\r\n\t\t\t\tfor (const char_t* bi = buffer[j].c_str(); *bi; ++bi)\r\n\t\t\t\t\t*ri++ = *bi;\r\n\r\n\t\t\t*ri = 0;\r\n\r\n\t\t\treturn xpath_string::from_heap_preallocated(result, ri);\r\n\t\t}\r\n\r\n\t\txpath_string eval_string(const xpath_context& c, const xpath_stack& stack)\r\n\t\t{\r\n\t\t\tswitch (_type)\r\n\t\t\t{\r\n\t\t\tcase ast_string_constant:\r\n\t\t\t\treturn xpath_string::from_const(_data.string);\r\n\r\n\t\t\tcase ast_func_local_name_0:\r\n\t\t\t{\r\n\t\t\t\txpath_node na = c.n;\r\n\r\n\t\t\t\treturn xpath_string::from_const(local_name(na));\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_local_name_1:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\txpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);\r\n\t\t\t\txpath_node na = ns.first();\r\n\r\n\t\t\t\treturn xpath_string::from_const(local_name(na));\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_name_0:\r\n\t\t\t{\r\n\t\t\t\txpath_node na = c.n;\r\n\r\n\t\t\t\treturn xpath_string::from_const(qualified_name(na));\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_name_1:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\txpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);\r\n\t\t\t\txpath_node na = ns.first();\r\n\r\n\t\t\t\treturn xpath_string::from_const(qualified_name(na));\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_namespace_uri_0:\r\n\t\t\t{\r\n\t\t\t\txpath_node na = c.n;\r\n\r\n\t\t\t\treturn xpath_string::from_const(namespace_uri(na));\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_namespace_uri_1:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.result);\r\n\r\n\t\t\t\txpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);\r\n\t\t\t\txpath_node na = ns.first();\r\n\r\n\t\t\t\treturn xpath_string::from_const(namespace_uri(na));\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_string_0:\r\n\t\t\t\treturn string_value(c.n, stack.result);\r\n\r\n\t\t\tcase ast_func_string_1:\r\n\t\t\t\treturn _left->eval_string(c, stack);\r\n\r\n\t\t\tcase ast_func_concat:\r\n\t\t\t\treturn eval_string_concat(c, stack);\r\n\r\n\t\t\tcase ast_func_substring_before:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.temp);\r\n\r\n\t\t\t\txpath_stack swapped_stack = {stack.temp, stack.result};\r\n\r\n\t\t\t\txpath_string s = _left->eval_string(c, swapped_stack);\r\n\t\t\t\txpath_string p = _right->eval_string(c, swapped_stack);\r\n\r\n\t\t\t\tconst char_t* pos = find_substring(s.c_str(), p.c_str());\r\n\r\n\t\t\t\treturn pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string();\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_substring_after:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.temp);\r\n\r\n\t\t\t\txpath_stack swapped_stack = {stack.temp, stack.result};\r\n\r\n\t\t\t\txpath_string s = _left->eval_string(c, swapped_stack);\r\n\t\t\t\txpath_string p = _right->eval_string(c, swapped_stack);\r\n\r\n\t\t\t\tconst char_t* pos = find_substring(s.c_str(), p.c_str());\r\n\t\t\t\tif (!pos) return xpath_string();\r\n\r\n\t\t\t\tconst char_t* rbegin = pos + p.length();\r\n\t\t\t\tconst char_t* rend = s.c_str() + s.length();\r\n\r\n\t\t\t\treturn s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin);\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_substring_2:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.temp);\r\n\r\n\t\t\t\txpath_stack swapped_stack = {stack.temp, stack.result};\r\n\r\n\t\t\t\txpath_string s = _left->eval_string(c, swapped_stack);\r\n\t\t\t\tsize_t s_length = s.length();\r\n\r\n\t\t\t\tdouble first = round_nearest(_right->eval_number(c, stack));\r\n\r\n\t\t\t\tif (is_nan(first)) return xpath_string(); // NaN\r\n\t\t\t\telse if (first >= static_cast<double>(s_length + 1)) return xpath_string();\r\n\r\n\t\t\t\tsize_t pos = first < 1 ? 1 : static_cast<size_t>(first);\r\n\t\t\t\tassert(1 <= pos && pos <= s_length + 1);\r\n\r\n\t\t\t\tconst char_t* rbegin = s.c_str() + (pos - 1);\r\n\t\t\t\tconst char_t* rend = s.c_str() + s.length();\r\n\r\n\t\t\t\treturn s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin);\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_substring_3:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.temp);\r\n\r\n\t\t\t\txpath_stack swapped_stack = {stack.temp, stack.result};\r\n\r\n\t\t\t\txpath_string s = _left->eval_string(c, swapped_stack);\r\n\t\t\t\tsize_t s_length = s.length();\r\n\r\n\t\t\t\tdouble first = round_nearest(_right->eval_number(c, stack));\r\n\t\t\t\tdouble last = first + round_nearest(_right->_next->eval_number(c, stack));\r\n\r\n\t\t\t\tif (is_nan(first) || is_nan(last)) return xpath_string();\r\n\t\t\t\telse if (first >= static_cast<double>(s_length + 1)) return xpath_string();\r\n\t\t\t\telse if (first >= last) return xpath_string();\r\n\t\t\t\telse if (last < 1) return xpath_string();\r\n\r\n\t\t\t\tsize_t pos = first < 1 ? 1 : static_cast<size_t>(first);\r\n\t\t\t\tsize_t end = last >= static_cast<double>(s_length + 1) ? s_length + 1 : static_cast<size_t>(last);\r\n\r\n\t\t\t\tassert(1 <= pos && pos <= end && end <= s_length + 1);\r\n\t\t\t\tconst char_t* rbegin = s.c_str() + (pos - 1);\r\n\t\t\t\tconst char_t* rend = s.c_str() + (end - 1);\r\n\r\n\t\t\t\treturn (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result);\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_normalize_space_0:\r\n\t\t\t{\r\n\t\t\t\txpath_string s = string_value(c.n, stack.result);\r\n\r\n\t\t\t\tchar_t* begin = s.data(stack.result);\r\n\t\t\t\tif (!begin) return xpath_string();\r\n\r\n\t\t\t\tchar_t* end = normalize_space(begin);\r\n\r\n\t\t\t\treturn xpath_string::from_heap_preallocated(begin, end);\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_normalize_space_1:\r\n\t\t\t{\r\n\t\t\t\txpath_string s = _left->eval_string(c, stack);\r\n\r\n\t\t\t\tchar_t* begin = s.data(stack.result);\r\n\t\t\t\tif (!begin) return xpath_string();\r\n\r\n\t\t\t\tchar_t* end = normalize_space(begin);\r\n\r\n\t\t\t\treturn xpath_string::from_heap_preallocated(begin, end);\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_translate:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.temp);\r\n\r\n\t\t\t\txpath_stack swapped_stack = {stack.temp, stack.result};\r\n\r\n\t\t\t\txpath_string s = _left->eval_string(c, stack);\r\n\t\t\t\txpath_string from = _right->eval_string(c, swapped_stack);\r\n\t\t\t\txpath_string to = _right->_next->eval_string(c, swapped_stack);\r\n\r\n\t\t\t\tchar_t* begin = s.data(stack.result);\r\n\t\t\t\tif (!begin) return xpath_string();\r\n\r\n\t\t\t\tchar_t* end = translate(begin, from.c_str(), to.c_str(), to.length());\r\n\r\n\t\t\t\treturn xpath_string::from_heap_preallocated(begin, end);\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_opt_translate_table:\r\n\t\t\t{\r\n\t\t\t\txpath_string s = _left->eval_string(c, stack);\r\n\r\n\t\t\t\tchar_t* begin = s.data(stack.result);\r\n\t\t\t\tif (!begin) return xpath_string();\r\n\r\n\t\t\t\tchar_t* end = translate_table(begin, _data.table);\r\n\r\n\t\t\t\treturn xpath_string::from_heap_preallocated(begin, end);\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_variable:\r\n\t\t\t{\r\n\t\t\t\tassert(_rettype == _data.variable->type());\r\n\r\n\t\t\t\tif (_rettype == xpath_type_string)\r\n\t\t\t\t\treturn xpath_string::from_const(_data.variable->get_string());\r\n\r\n\t\t\t\t// variable needs to be converted to the correct type, this is handled by the fallthrough block below\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\t;\r\n\t\t\t}\r\n\r\n\t\t\t// none of the ast types that return the value directly matched, we need to perform type conversion\r\n\t\t\tswitch (_rettype)\r\n\t\t\t{\r\n\t\t\tcase xpath_type_boolean:\r\n\t\t\t\treturn xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT(\"true\") : PUGIXML_TEXT(\"false\"));\r\n\r\n\t\t\tcase xpath_type_number:\r\n\t\t\t\treturn convert_number_to_string(eval_number(c, stack), stack.result);\r\n\r\n\t\t\tcase xpath_type_node_set:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.temp);\r\n\r\n\t\t\t\txpath_stack swapped_stack = {stack.temp, stack.result};\r\n\r\n\t\t\t\txpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first);\r\n\t\t\t\treturn ns.empty() ? xpath_string() : string_value(ns.first(), stack.result);\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tassert(false && \"Wrong expression for return type string\"); // unreachable\r\n\t\t\t\treturn xpath_string();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\txpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval)\r\n\t\t{\r\n\t\t\tswitch (_type)\r\n\t\t\t{\r\n\t\t\tcase ast_op_union:\r\n\t\t\t{\r\n\t\t\t\txpath_allocator_capture cr(stack.temp);\r\n\r\n\t\t\t\txpath_stack swapped_stack = {stack.temp, stack.result};\r\n\r\n\t\t\t\txpath_node_set_raw ls = _left->eval_node_set(c, stack, eval);\r\n\t\t\t\txpath_node_set_raw rs = _right->eval_node_set(c, swapped_stack, eval);\r\n\r\n\t\t\t\t// we can optimize merging two sorted sets, but this is a very rare operation, so don't bother\r\n\t\t\t\tls.set_type(xpath_node_set::type_unsorted);\r\n\r\n\t\t\t\tls.append(rs.begin(), rs.end(), stack.result);\r\n\t\t\t\tls.remove_duplicates(stack.temp);\r\n\r\n\t\t\t\treturn ls;\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_filter:\r\n\t\t\t{\r\n\t\t\t\txpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all);\r\n\r\n\t\t\t\t// either expression is a number or it contains position() call; sort by document order\r\n\t\t\t\tif (_test != predicate_posinv) set.sort_do();\r\n\r\n\t\t\t\tbool once = eval_once(set.type(), eval);\r\n\r\n\t\t\t\tapply_predicate(set, 0, stack, once);\r\n\r\n\t\t\t\treturn set;\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_func_id:\r\n\t\t\t\treturn xpath_node_set_raw();\r\n\r\n\t\t\tcase ast_step:\r\n\t\t\t{\r\n\t\t\t\tswitch (_axis)\r\n\t\t\t\t{\r\n\t\t\t\tcase axis_ancestor:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_ancestor>());\r\n\r\n\t\t\t\tcase axis_ancestor_or_self:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_ancestor_or_self>());\r\n\r\n\t\t\t\tcase axis_attribute:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_attribute>());\r\n\r\n\t\t\t\tcase axis_child:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_child>());\r\n\r\n\t\t\t\tcase axis_descendant:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_descendant>());\r\n\r\n\t\t\t\tcase axis_descendant_or_self:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_descendant_or_self>());\r\n\r\n\t\t\t\tcase axis_following:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_following>());\r\n\r\n\t\t\t\tcase axis_following_sibling:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_following_sibling>());\r\n\r\n\t\t\t\tcase axis_namespace:\r\n\t\t\t\t\t// namespaced axis is not supported\r\n\t\t\t\t\treturn xpath_node_set_raw();\r\n\r\n\t\t\t\tcase axis_parent:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_parent>());\r\n\r\n\t\t\t\tcase axis_preceding:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_preceding>());\r\n\r\n\t\t\t\tcase axis_preceding_sibling:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_preceding_sibling>());\r\n\r\n\t\t\t\tcase axis_self:\r\n\t\t\t\t\treturn step_do(c, stack, eval, axis_to_type<axis_self>());\r\n\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tassert(false && \"Unknown axis\"); // unreachable\r\n\t\t\t\t\treturn xpath_node_set_raw();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_step_root:\r\n\t\t\t{\r\n\t\t\t\tassert(!_right); // root step can't have any predicates\r\n\r\n\t\t\t\txpath_node_set_raw ns;\r\n\r\n\t\t\t\tns.set_type(xpath_node_set::type_sorted);\r\n\r\n\t\t\t\tif (c.n.node()) ns.push_back(c.n.node().root(), stack.result);\r\n\t\t\t\telse if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result);\r\n\r\n\t\t\t\treturn ns;\r\n\t\t\t}\r\n\r\n\t\t\tcase ast_variable:\r\n\t\t\t{\r\n\t\t\t\tassert(_rettype == _data.variable->type());\r\n\r\n\t\t\t\tif (_rettype == xpath_type_node_set)\r\n\t\t\t\t{\r\n\t\t\t\t\tconst xpath_node_set& s = _data.variable->get_node_set();\r\n\r\n\t\t\t\t\txpath_node_set_raw ns;\r\n\r\n\t\t\t\t\tns.set_type(s.type());\r\n\t\t\t\t\tns.append(s.begin(), s.end(), stack.result);\r\n\r\n\t\t\t\t\treturn ns;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// variable needs to be converted to the correct type, this is handled by the fallthrough block below\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\t;\r\n\t\t\t}\r\n\r\n\t\t\t// none of the ast types that return the value directly matched, but conversions to node set are invalid\r\n\t\t\tassert(false && \"Wrong expression for return type node set\"); // unreachable\r\n\t\t\treturn xpath_node_set_raw();\r\n\t\t}\r\n\r\n\t\tvoid optimize(xpath_allocator* alloc)\r\n\t\t{\r\n\t\t\tif (_left)\r\n\t\t\t\t_left->optimize(alloc);\r\n\r\n\t\t\tif (_right)\r\n\t\t\t\t_right->optimize(alloc);\r\n\r\n\t\t\tif (_next)\r\n\t\t\t\t_next->optimize(alloc);\r\n\r\n\t\t\t// coverity[var_deref_model]\r\n\t\t\toptimize_self(alloc);\r\n\t\t}\r\n\r\n\t\tvoid optimize_self(xpath_allocator* alloc)\r\n\t\t{\r\n\t\t\t// Rewrite [position()=expr] with [expr]\r\n\t\t\t// Note that this step has to go before classification to recognize [position()=1]\r\n\t\t\tif ((_type == ast_filter || _type == ast_predicate) &&\r\n\t\t\t\t_right && // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate)\r\n\t\t\t\t_right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number)\r\n\t\t\t{\r\n\t\t\t\t_right = _right->_right;\r\n\t\t\t}\r\n\r\n\t\t\t// Classify filter/predicate ops to perform various optimizations during evaluation\r\n\t\t\tif ((_type == ast_filter || _type == ast_predicate) && _right) // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate)\r\n\t\t\t{\r\n\t\t\t\tassert(_test == predicate_default);\r\n\r\n\t\t\t\tif (_right->_type == ast_number_constant && _right->_data.number == 1.0)\r\n\t\t\t\t\t_test = predicate_constant_one;\r\n\t\t\t\telse if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last))\r\n\t\t\t\t\t_test = predicate_constant;\r\n\t\t\t\telse if (_right->_rettype != xpath_type_number && _right->is_posinv_expr())\r\n\t\t\t\t\t_test = predicate_posinv;\r\n\t\t\t}\r\n\r\n\t\t\t// Rewrite descendant-or-self::node()/child::foo with descendant::foo\r\n\t\t\t// The former is a full form of //foo, the latter is much faster since it executes the node test immediately\r\n\t\t\t// Do a similar kind of rewrite for self/descendant/descendant-or-self axes\r\n\t\t\t// Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1])\r\n\t\t\tif (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) &&\r\n\t\t\t\t_left && _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right &&\r\n\t\t\t\tis_posinv_step())\r\n\t\t\t{\r\n\t\t\t\tif (_axis == axis_child || _axis == axis_descendant)\r\n\t\t\t\t\t_axis = axis_descendant;\r\n\t\t\t\telse\r\n\t\t\t\t\t_axis = axis_descendant_or_self;\r\n\r\n\t\t\t\t_left = _left->_left;\r\n\t\t\t}\r\n\r\n\t\t\t// Use optimized lookup table implementation for translate() with constant arguments\r\n\t\t\tif (_type == ast_func_translate &&\r\n\t\t\t\t_right && // workaround for clang static analyzer (_right is never null for ast_func_translate)\r\n\t\t\t\t_right->_type == ast_string_constant && _right->_next->_type == ast_string_constant)\r\n\t\t\t{\r\n\t\t\t\tunsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string);\r\n\r\n\t\t\t\tif (table)\r\n\t\t\t\t{\r\n\t\t\t\t\t_type = ast_opt_translate_table;\r\n\t\t\t\t\t_data.table = table;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Use optimized path for @attr = 'value' or @attr = $value\r\n\t\t\tif (_type == ast_op_equal &&\r\n\t\t\t\t_left && _right && // workaround for clang static analyzer and Coverity (_left and _right are never null for ast_op_equal)\r\n                // coverity[mixed_enums]\r\n\t\t\t\t_left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right &&\r\n\t\t\t\t(_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string)))\r\n\t\t\t{\r\n\t\t\t\t_type = ast_opt_compare_attribute;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tbool is_posinv_expr() const\r\n\t\t{\r\n\t\t\tswitch (_type)\r\n\t\t\t{\r\n\t\t\tcase ast_func_position:\r\n\t\t\tcase ast_func_last:\r\n\t\t\t\treturn false;\r\n\r\n\t\t\tcase ast_string_constant:\r\n\t\t\tcase ast_number_constant:\r\n\t\t\tcase ast_variable:\r\n\t\t\t\treturn true;\r\n\r\n\t\t\tcase ast_step:\r\n\t\t\tcase ast_step_root:\r\n\t\t\t\treturn true;\r\n\r\n\t\t\tcase ast_predicate:\r\n\t\t\tcase ast_filter:\r\n\t\t\t\treturn true;\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tif (_left && !_left->is_posinv_expr()) return false;\r\n\r\n\t\t\t\tfor (xpath_ast_node* n = _right; n; n = n->_next)\r\n\t\t\t\t\tif (!n->is_posinv_expr()) return false;\r\n\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tbool is_posinv_step() const\r\n\t\t{\r\n\t\t\tassert(_type == ast_step);\r\n\r\n\t\t\tfor (xpath_ast_node* n = _right; n; n = n->_next)\r\n\t\t\t{\r\n\t\t\t\tassert(n->_type == ast_predicate);\r\n\r\n\t\t\t\tif (n->_test != predicate_posinv)\r\n\t\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\txpath_value_type rettype() const\r\n\t\t{\r\n\t\t\treturn static_cast<xpath_value_type>(_rettype);\r\n\t\t}\r\n\t};\r\n\r\n\tstatic const size_t xpath_ast_depth_limit =\r\n\t#ifdef PUGIXML_XPATH_DEPTH_LIMIT\r\n\t\tPUGIXML_XPATH_DEPTH_LIMIT\r\n\t#else\r\n\t\t1024\r\n\t#endif\r\n\t\t;\r\n\r\n\tstruct xpath_parser\r\n\t{\r\n\t\txpath_allocator* _alloc;\r\n\t\txpath_lexer _lexer;\r\n\r\n\t\tconst char_t* _query;\r\n\t\txpath_variable_set* _variables;\r\n\r\n\t\txpath_parse_result* _result;\r\n\r\n\t\tchar_t _scratch[32];\r\n\r\n\t\tsize_t _depth;\r\n\r\n\t\txpath_ast_node* error(const char* message)\r\n\t\t{\r\n\t\t\t_result->error = message;\r\n\t\t\t_result->offset = _lexer.current_pos() - _query;\r\n\r\n\t\t\treturn 0;\r\n\t\t}\r\n\r\n\t\txpath_ast_node* error_oom()\r\n\t\t{\r\n\t\t\tassert(_alloc->_error);\r\n\t\t\t*_alloc->_error = true;\r\n\r\n\t\t\treturn 0;\r\n\t\t}\r\n\r\n\t\txpath_ast_node* error_rec()\r\n\t\t{\r\n\t\t\treturn error(\"Exceeded maximum allowed query depth\");\r\n\t\t}\r\n\r\n\t\tvoid* alloc_node()\r\n\t\t{\r\n\t\t\treturn _alloc->allocate(sizeof(xpath_ast_node));\r\n\t\t}\r\n\r\n\t\txpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, const char_t* value)\r\n\t\t{\r\n\t\t\tvoid* memory = alloc_node();\r\n\t\t\treturn memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;\r\n\t\t}\r\n\r\n\t\txpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, double value)\r\n\t\t{\r\n\t\t\tvoid* memory = alloc_node();\r\n\t\t\treturn memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;\r\n\t\t}\r\n\r\n\t\txpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value)\r\n\t\t{\r\n\t\t\tvoid* memory = alloc_node();\r\n\t\t\treturn memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;\r\n\t\t}\r\n\r\n\t\txpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0)\r\n\t\t{\r\n\t\t\tvoid* memory = alloc_node();\r\n\t\t\treturn memory ? new (memory) xpath_ast_node(type, rettype, left, right) : 0;\r\n\t\t}\r\n\r\n\t\txpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents)\r\n\t\t{\r\n\t\t\tvoid* memory = alloc_node();\r\n\t\t\treturn memory ? new (memory) xpath_ast_node(type, left, axis, test, contents) : 0;\r\n\t\t}\r\n\r\n\t\txpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test)\r\n\t\t{\r\n\t\t\tvoid* memory = alloc_node();\r\n\t\t\treturn memory ? new (memory) xpath_ast_node(type, left, right, test) : 0;\r\n\t\t}\r\n\r\n\t\tconst char_t* alloc_string(const xpath_lexer_string& value)\r\n\t\t{\r\n\t\t\tif (!value.begin)\r\n\t\t\t\treturn PUGIXML_TEXT(\"\");\r\n\r\n\t\t\tsize_t length = static_cast<size_t>(value.end - value.begin);\r\n\r\n\t\t\tchar_t* c = static_cast<char_t*>(_alloc->allocate((length + 1) * sizeof(char_t)));\r\n\t\t\tif (!c) return 0;\r\n\r\n\t\t\tmemcpy(c, value.begin, length * sizeof(char_t));\r\n\t\t\tc[length] = 0;\r\n\r\n\t\t\treturn c;\r\n\t\t}\r\n\r\n\t\txpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2])\r\n\t\t{\r\n\t\t\tswitch (name.begin[0])\r\n\t\t\t{\r\n\t\t\tcase 'b':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"boolean\") && argc == 1)\r\n\t\t\t\t\treturn alloc_node(ast_func_boolean, xpath_type_boolean, args[0]);\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'c':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"count\") && argc == 1)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (args[0]->rettype() != xpath_type_node_set) return error(\"Function has to be applied to node set\");\r\n\t\t\t\t\treturn alloc_node(ast_func_count, xpath_type_number, args[0]);\r\n\t\t\t\t}\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"contains\") && argc == 2)\r\n\t\t\t\t\treturn alloc_node(ast_func_contains, xpath_type_boolean, args[0], args[1]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"concat\") && argc >= 2)\r\n\t\t\t\t\treturn alloc_node(ast_func_concat, xpath_type_string, args[0], args[1]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"ceiling\") && argc == 1)\r\n\t\t\t\t\treturn alloc_node(ast_func_ceiling, xpath_type_number, args[0]);\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'f':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"false\") && argc == 0)\r\n\t\t\t\t\treturn alloc_node(ast_func_false, xpath_type_boolean);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"floor\") && argc == 1)\r\n\t\t\t\t\treturn alloc_node(ast_func_floor, xpath_type_number, args[0]);\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'i':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"id\") && argc == 1)\r\n\t\t\t\t\treturn alloc_node(ast_func_id, xpath_type_node_set, args[0]);\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'l':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"last\") && argc == 0)\r\n\t\t\t\t\treturn alloc_node(ast_func_last, xpath_type_number);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"lang\") && argc == 1)\r\n\t\t\t\t\treturn alloc_node(ast_func_lang, xpath_type_boolean, args[0]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"local-name\") && argc <= 1)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error(\"Function has to be applied to node set\");\r\n\t\t\t\t\treturn alloc_node(argc == 0 ? ast_func_local_name_0 : ast_func_local_name_1, xpath_type_string, args[0]);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'n':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"name\") && argc <= 1)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error(\"Function has to be applied to node set\");\r\n\t\t\t\t\treturn alloc_node(argc == 0 ? ast_func_name_0 : ast_func_name_1, xpath_type_string, args[0]);\r\n\t\t\t\t}\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"namespace-uri\") && argc <= 1)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error(\"Function has to be applied to node set\");\r\n\t\t\t\t\treturn alloc_node(argc == 0 ? ast_func_namespace_uri_0 : ast_func_namespace_uri_1, xpath_type_string, args[0]);\r\n\t\t\t\t}\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"normalize-space\") && argc <= 1)\r\n\t\t\t\t\treturn alloc_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"not\") && argc == 1)\r\n\t\t\t\t\treturn alloc_node(ast_func_not, xpath_type_boolean, args[0]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"number\") && argc <= 1)\r\n\t\t\t\t\treturn alloc_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]);\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'p':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"position\") && argc == 0)\r\n\t\t\t\t\treturn alloc_node(ast_func_position, xpath_type_number);\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'r':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"round\") && argc == 1)\r\n\t\t\t\t\treturn alloc_node(ast_func_round, xpath_type_number, args[0]);\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 's':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"string\") && argc <= 1)\r\n\t\t\t\t\treturn alloc_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"string-length\") && argc <= 1)\r\n\t\t\t\t\treturn alloc_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"starts-with\") && argc == 2)\r\n\t\t\t\t\treturn alloc_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"substring-before\") && argc == 2)\r\n\t\t\t\t\treturn alloc_node(ast_func_substring_before, xpath_type_string, args[0], args[1]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"substring-after\") && argc == 2)\r\n\t\t\t\t\treturn alloc_node(ast_func_substring_after, xpath_type_string, args[0], args[1]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"substring\") && (argc == 2 || argc == 3))\r\n\t\t\t\t\treturn alloc_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"sum\") && argc == 1)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (args[0]->rettype() != xpath_type_node_set) return error(\"Function has to be applied to node set\");\r\n\t\t\t\t\treturn alloc_node(ast_func_sum, xpath_type_number, args[0]);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 't':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"translate\") && argc == 3)\r\n\t\t\t\t\treturn alloc_node(ast_func_translate, xpath_type_string, args[0], args[1]);\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"true\") && argc == 0)\r\n\t\t\t\t\treturn alloc_node(ast_func_true, xpath_type_boolean);\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\treturn error(\"Unrecognized function or wrong parameter count\");\r\n\t\t}\r\n\r\n\t\taxis_t parse_axis_name(const xpath_lexer_string& name, bool& specified)\r\n\t\t{\r\n\t\t\tspecified = true;\r\n\r\n\t\t\tswitch (name.begin[0])\r\n\t\t\t{\r\n\t\t\tcase 'a':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"ancestor\"))\r\n\t\t\t\t\treturn axis_ancestor;\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"ancestor-or-self\"))\r\n\t\t\t\t\treturn axis_ancestor_or_self;\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"attribute\"))\r\n\t\t\t\t\treturn axis_attribute;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'c':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"child\"))\r\n\t\t\t\t\treturn axis_child;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'd':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"descendant\"))\r\n\t\t\t\t\treturn axis_descendant;\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"descendant-or-self\"))\r\n\t\t\t\t\treturn axis_descendant_or_self;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'f':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"following\"))\r\n\t\t\t\t\treturn axis_following;\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"following-sibling\"))\r\n\t\t\t\t\treturn axis_following_sibling;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'n':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"namespace\"))\r\n\t\t\t\t\treturn axis_namespace;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'p':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"parent\"))\r\n\t\t\t\t\treturn axis_parent;\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"preceding\"))\r\n\t\t\t\t\treturn axis_preceding;\r\n\t\t\t\telse if (name == PUGIXML_TEXT(\"preceding-sibling\"))\r\n\t\t\t\t\treturn axis_preceding_sibling;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 's':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"self\"))\r\n\t\t\t\t\treturn axis_self;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\tspecified = false;\r\n\t\t\treturn axis_child;\r\n\t\t}\r\n\r\n\t\tnodetest_t parse_node_test_type(const xpath_lexer_string& name)\r\n\t\t{\r\n\t\t\tswitch (name.begin[0])\r\n\t\t\t{\r\n\t\t\tcase 'c':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"comment\"))\r\n\t\t\t\t\treturn nodetest_type_comment;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'n':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"node\"))\r\n\t\t\t\t\treturn nodetest_type_node;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 'p':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"processing-instruction\"))\r\n\t\t\t\t\treturn nodetest_type_pi;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tcase 't':\r\n\t\t\t\tif (name == PUGIXML_TEXT(\"text\"))\r\n\t\t\t\t\treturn nodetest_type_text;\r\n\r\n\t\t\t\tbreak;\r\n\r\n\t\t\tdefault:\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\treturn nodetest_none;\r\n\t\t}\r\n\r\n\t\t// PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall\r\n\t\txpath_ast_node* parse_primary_expression()\r\n\t\t{\r\n\t\t\tswitch (_lexer.current())\r\n\t\t\t{\r\n\t\t\tcase lex_var_ref:\r\n\t\t\t{\r\n\t\t\t\txpath_lexer_string name = _lexer.contents();\r\n\r\n\t\t\t\tif (!_variables)\r\n\t\t\t\t\treturn error(\"Unknown variable: variable set is not provided\");\r\n\r\n\t\t\t\txpath_variable* var = 0;\r\n\t\t\t\tif (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var))\r\n\t\t\t\t\treturn error_oom();\r\n\r\n\t\t\t\tif (!var)\r\n\t\t\t\t\treturn error(\"Unknown variable: variable set does not contain the given name\");\r\n\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\treturn alloc_node(ast_variable, var->type(), var);\r\n\t\t\t}\r\n\r\n\t\t\tcase lex_open_brace:\r\n\t\t\t{\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\txpath_ast_node* n = parse_expression();\r\n\t\t\t\tif (!n) return 0;\r\n\r\n\t\t\t\tif (_lexer.current() != lex_close_brace)\r\n\t\t\t\t\treturn error(\"Expected ')' to match an opening '('\");\r\n\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\treturn n;\r\n\t\t\t}\r\n\r\n\t\t\tcase lex_quoted_string:\r\n\t\t\t{\r\n\t\t\t\tconst char_t* value = alloc_string(_lexer.contents());\r\n\t\t\t\tif (!value) return 0;\r\n\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\treturn alloc_node(ast_string_constant, xpath_type_string, value);\r\n\t\t\t}\r\n\r\n\t\t\tcase lex_number:\r\n\t\t\t{\r\n\t\t\t\tdouble value = 0;\r\n\r\n\t\t\t\tif (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value))\r\n\t\t\t\t\treturn error_oom();\r\n\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\treturn alloc_node(ast_number_constant, xpath_type_number, value);\r\n\t\t\t}\r\n\r\n\t\t\tcase lex_string:\r\n\t\t\t{\r\n\t\t\t\txpath_ast_node* args[2] = {0};\r\n\t\t\t\tsize_t argc = 0;\r\n\r\n\t\t\t\txpath_lexer_string function = _lexer.contents();\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\txpath_ast_node* last_arg = 0;\r\n\r\n\t\t\t\tif (_lexer.current() != lex_open_brace)\r\n\t\t\t\t\treturn error(\"Unrecognized function call\");\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\tsize_t old_depth = _depth;\r\n\r\n\t\t\t\twhile (_lexer.current() != lex_close_brace)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (argc > 0)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (_lexer.current() != lex_comma)\r\n\t\t\t\t\t\t\treturn error(\"No comma between function arguments\");\r\n\t\t\t\t\t\t_lexer.next();\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif (++_depth > xpath_ast_depth_limit)\r\n\t\t\t\t\t\treturn error_rec();\r\n\r\n\t\t\t\t\txpath_ast_node* n = parse_expression();\r\n\t\t\t\t\tif (!n) return 0;\r\n\r\n\t\t\t\t\tif (argc < 2) args[argc] = n;\r\n\t\t\t\t\telse last_arg->set_next(n);\r\n\r\n\t\t\t\t\targc++;\r\n\t\t\t\t\tlast_arg = n;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\t_depth = old_depth;\r\n\r\n\t\t\t\treturn parse_function(function, argc, args);\r\n\t\t\t}\r\n\r\n\t\t\tdefault:\r\n\t\t\t\treturn error(\"Unrecognizable primary expression\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// FilterExpr ::= PrimaryExpr | FilterExpr Predicate\r\n\t\t// Predicate ::= '[' PredicateExpr ']'\r\n\t\t// PredicateExpr ::= Expr\r\n\t\txpath_ast_node* parse_filter_expression()\r\n\t\t{\r\n\t\t\txpath_ast_node* n = parse_primary_expression();\r\n\t\t\tif (!n) return 0;\r\n\r\n\t\t\tsize_t old_depth = _depth;\r\n\r\n\t\t\twhile (_lexer.current() == lex_open_square_brace)\r\n\t\t\t{\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\tif (++_depth > xpath_ast_depth_limit)\r\n\t\t\t\t\treturn error_rec();\r\n\r\n\t\t\t\tif (n->rettype() != xpath_type_node_set)\r\n\t\t\t\t\treturn error(\"Predicate has to be applied to node set\");\r\n\r\n\t\t\t\txpath_ast_node* expr = parse_expression();\r\n\t\t\t\tif (!expr) return 0;\r\n\r\n\t\t\t\tn = alloc_node(ast_filter, n, expr, predicate_default);\r\n\t\t\t\tif (!n) return 0;\r\n\r\n\t\t\t\tif (_lexer.current() != lex_close_square_brace)\r\n\t\t\t\t\treturn error(\"Expected ']' to match an opening '['\");\r\n\r\n\t\t\t\t_lexer.next();\r\n\t\t\t}\r\n\r\n\t\t\t_depth = old_depth;\r\n\r\n\t\t\treturn n;\r\n\t\t}\r\n\r\n\t\t// Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep\r\n\t\t// AxisSpecifier ::= AxisName '::' | '@'?\r\n\t\t// NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')'\r\n\t\t// NameTest ::= '*' | NCName ':' '*' | QName\r\n\t\t// AbbreviatedStep ::= '.' | '..'\r\n\t\txpath_ast_node* parse_step(xpath_ast_node* set)\r\n\t\t{\r\n\t\t\tif (set && set->rettype() != xpath_type_node_set)\r\n\t\t\t\treturn error(\"Step has to be applied to node set\");\r\n\r\n\t\t\tbool axis_specified = false;\r\n\t\t\taxis_t axis = axis_child; // implied child axis\r\n\r\n\t\t\tif (_lexer.current() == lex_axis_attribute)\r\n\t\t\t{\r\n\t\t\t\taxis = axis_attribute;\r\n\t\t\t\taxis_specified = true;\r\n\r\n\t\t\t\t_lexer.next();\r\n\t\t\t}\r\n\t\t\telse if (_lexer.current() == lex_dot)\r\n\t\t\t{\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\tif (_lexer.current() == lex_open_square_brace)\r\n\t\t\t\t\treturn error(\"Predicates are not allowed after an abbreviated step\");\r\n\r\n\t\t\t\treturn alloc_node(ast_step, set, axis_self, nodetest_type_node, 0);\r\n\t\t\t}\r\n\t\t\telse if (_lexer.current() == lex_double_dot)\r\n\t\t\t{\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\tif (_lexer.current() == lex_open_square_brace)\r\n\t\t\t\t\treturn error(\"Predicates are not allowed after an abbreviated step\");\r\n\r\n\t\t\t\treturn alloc_node(ast_step, set, axis_parent, nodetest_type_node, 0);\r\n\t\t\t}\r\n\r\n\t\t\tnodetest_t nt_type = nodetest_none;\r\n\t\t\txpath_lexer_string nt_name;\r\n\r\n\t\t\tif (_lexer.current() == lex_string)\r\n\t\t\t{\r\n\t\t\t\t// node name test\r\n\t\t\t\tnt_name = _lexer.contents();\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\t// was it an axis name?\r\n\t\t\t\tif (_lexer.current() == lex_double_colon)\r\n\t\t\t\t{\r\n\t\t\t\t\t// parse axis name\r\n\t\t\t\t\tif (axis_specified)\r\n\t\t\t\t\t\treturn error(\"Two axis specifiers in one step\");\r\n\r\n\t\t\t\t\taxis = parse_axis_name(nt_name, axis_specified);\r\n\r\n\t\t\t\t\tif (!axis_specified)\r\n\t\t\t\t\t\treturn error(\"Unknown axis\");\r\n\r\n\t\t\t\t\t// read actual node test\r\n\t\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\t\tif (_lexer.current() == lex_multiply)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tnt_type = nodetest_all;\r\n\t\t\t\t\t\tnt_name = xpath_lexer_string();\r\n\t\t\t\t\t\t_lexer.next();\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse if (_lexer.current() == lex_string)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tnt_name = _lexer.contents();\r\n\t\t\t\t\t\t_lexer.next();\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\treturn error(\"Unrecognized node test\");\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (nt_type == nodetest_none)\r\n\t\t\t\t{\r\n\t\t\t\t\t// node type test or processing-instruction\r\n\t\t\t\t\tif (_lexer.current() == lex_open_brace)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\t\t\tif (_lexer.current() == lex_close_brace)\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\t\t\t\tnt_type = parse_node_test_type(nt_name);\r\n\r\n\t\t\t\t\t\t\tif (nt_type == nodetest_none)\r\n\t\t\t\t\t\t\t\treturn error(\"Unrecognized node type\");\r\n\r\n\t\t\t\t\t\t\tnt_name = xpath_lexer_string();\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse if (nt_name == PUGIXML_TEXT(\"processing-instruction\"))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif (_lexer.current() != lex_quoted_string)\r\n\t\t\t\t\t\t\t\treturn error(\"Only literals are allowed as arguments to processing-instruction()\");\r\n\r\n\t\t\t\t\t\t\tnt_type = nodetest_pi;\r\n\t\t\t\t\t\t\tnt_name = _lexer.contents();\r\n\t\t\t\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\t\t\t\tif (_lexer.current() != lex_close_brace)\r\n\t\t\t\t\t\t\t\treturn error(\"Unmatched brace near processing-instruction()\");\r\n\t\t\t\t\t\t\t_lexer.next();\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\treturn error(\"Unmatched brace near node type test\");\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\t// QName or NCName:*\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:*\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tnt_name.end--; // erase *\r\n\r\n\t\t\t\t\t\t\tnt_type = nodetest_all_in_namespace;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tnt_type = nodetest_name;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (_lexer.current() == lex_multiply)\r\n\t\t\t{\r\n\t\t\t\tnt_type = nodetest_all;\r\n\t\t\t\t_lexer.next();\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\treturn error(\"Unrecognized node test\");\r\n\t\t\t}\r\n\r\n\t\t\tconst char_t* nt_name_copy = alloc_string(nt_name);\r\n\t\t\tif (!nt_name_copy) return 0;\r\n\r\n\t\t\txpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy);\r\n\t\t\tif (!n) return 0;\r\n\r\n\t\t\tsize_t old_depth = _depth;\r\n\r\n\t\t\txpath_ast_node* last = 0;\r\n\r\n\t\t\twhile (_lexer.current() == lex_open_square_brace)\r\n\t\t\t{\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\tif (++_depth > xpath_ast_depth_limit)\r\n\t\t\t\t\treturn error_rec();\r\n\r\n\t\t\t\txpath_ast_node* expr = parse_expression();\r\n\t\t\t\tif (!expr) return 0;\r\n\r\n\t\t\t\txpath_ast_node* pred = alloc_node(ast_predicate, 0, expr, predicate_default);\r\n\t\t\t\tif (!pred) return 0;\r\n\r\n\t\t\t\tif (_lexer.current() != lex_close_square_brace)\r\n\t\t\t\t\treturn error(\"Expected ']' to match an opening '['\");\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\tif (last) last->set_next(pred);\r\n\t\t\t\telse n->set_right(pred);\r\n\r\n\t\t\t\tlast = pred;\r\n\t\t\t}\r\n\r\n\t\t\t_depth = old_depth;\r\n\r\n\t\t\treturn n;\r\n\t\t}\r\n\r\n\t\t// RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step\r\n\t\txpath_ast_node* parse_relative_location_path(xpath_ast_node* set)\r\n\t\t{\r\n\t\t\txpath_ast_node* n = parse_step(set);\r\n\t\t\tif (!n) return 0;\r\n\r\n\t\t\tsize_t old_depth = _depth;\r\n\r\n\t\t\twhile (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)\r\n\t\t\t{\r\n\t\t\t\tlexeme_t l = _lexer.current();\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\tif (l == lex_double_slash)\r\n\t\t\t\t{\r\n\t\t\t\t\tn = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);\r\n\t\t\t\t\tif (!n) return 0;\r\n\r\n\t\t\t\t\t++_depth;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (++_depth > xpath_ast_depth_limit)\r\n\t\t\t\t\treturn error_rec();\r\n\r\n\t\t\t\tn = parse_step(n);\r\n\t\t\t\tif (!n) return 0;\r\n\t\t\t}\r\n\r\n\t\t\t_depth = old_depth;\r\n\r\n\t\t\treturn n;\r\n\t\t}\r\n\r\n\t\t// LocationPath ::= RelativeLocationPath | AbsoluteLocationPath\r\n\t\t// AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath\r\n\t\txpath_ast_node* parse_location_path()\r\n\t\t{\r\n\t\t\tif (_lexer.current() == lex_slash)\r\n\t\t\t{\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\txpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set);\r\n\t\t\t\tif (!n) return 0;\r\n\r\n\t\t\t\t// relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path\r\n\t\t\t\tlexeme_t l = _lexer.current();\r\n\r\n\t\t\t\tif (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply)\r\n\t\t\t\t\treturn parse_relative_location_path(n);\r\n\t\t\t\telse\r\n\t\t\t\t\treturn n;\r\n\t\t\t}\r\n\t\t\telse if (_lexer.current() == lex_double_slash)\r\n\t\t\t{\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\txpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set);\r\n\t\t\t\tif (!n) return 0;\r\n\r\n\t\t\t\tn = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);\r\n\t\t\t\tif (!n) return 0;\r\n\r\n\t\t\t\treturn parse_relative_location_path(n);\r\n\t\t\t}\r\n\r\n\t\t\t// else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1\r\n\t\t\treturn parse_relative_location_path(0);\r\n\t\t}\r\n\r\n\t\t// PathExpr ::= LocationPath\r\n\t\t//\t\t\t\t| FilterExpr\r\n\t\t//\t\t\t\t| FilterExpr '/' RelativeLocationPath\r\n\t\t//\t\t\t\t| FilterExpr '//' RelativeLocationPath\r\n\t\t// UnionExpr ::= PathExpr | UnionExpr '|' PathExpr\r\n\t\t// UnaryExpr ::= UnionExpr | '-' UnaryExpr\r\n\t\txpath_ast_node* parse_path_or_unary_expression()\r\n\t\t{\r\n\t\t\t// Clarification.\r\n\t\t\t// PathExpr begins with either LocationPath or FilterExpr.\r\n\t\t\t// FilterExpr begins with PrimaryExpr\r\n\t\t\t// PrimaryExpr begins with '$' in case of it being a variable reference,\r\n\t\t\t// '(' in case of it being an expression, string literal, number constant or\r\n\t\t\t// function call.\r\n\t\t\tif (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace ||\r\n\t\t\t\t_lexer.current() == lex_quoted_string || _lexer.current() == lex_number ||\r\n\t\t\t\t_lexer.current() == lex_string)\r\n\t\t\t{\r\n\t\t\t\tif (_lexer.current() == lex_string)\r\n\t\t\t\t{\r\n\t\t\t\t\t// This is either a function call, or not - if not, we shall proceed with location path\r\n\t\t\t\t\tconst char_t* state = _lexer.state();\r\n\r\n\t\t\t\t\twhile (PUGI_IMPL_IS_CHARTYPE(*state, ct_space)) ++state;\r\n\r\n\t\t\t\t\tif (*state != '(')\r\n\t\t\t\t\t\treturn parse_location_path();\r\n\r\n\t\t\t\t\t// This looks like a function call; however this still can be a node-test. Check it.\r\n\t\t\t\t\tif (parse_node_test_type(_lexer.contents()) != nodetest_none)\r\n\t\t\t\t\t\treturn parse_location_path();\r\n\t\t\t\t}\r\n\r\n\t\t\t\txpath_ast_node* n = parse_filter_expression();\r\n\t\t\t\tif (!n) return 0;\r\n\r\n\t\t\t\tif (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)\r\n\t\t\t\t{\r\n\t\t\t\t\tlexeme_t l = _lexer.current();\r\n\t\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\t\tif (l == lex_double_slash)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (n->rettype() != xpath_type_node_set)\r\n\t\t\t\t\t\t\treturn error(\"Step has to be applied to node set\");\r\n\r\n\t\t\t\t\t\tn = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);\r\n\t\t\t\t\t\tif (!n) return 0;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t// select from location path\r\n\t\t\t\t\treturn parse_relative_location_path(n);\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn n;\r\n\t\t\t}\r\n\t\t\telse if (_lexer.current() == lex_minus)\r\n\t\t\t{\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\t// precedence 7+ - only parses union expressions\r\n\t\t\t\txpath_ast_node* n = parse_expression(7);\r\n\t\t\t\tif (!n) return 0;\r\n\r\n\t\t\t\treturn alloc_node(ast_op_negate, xpath_type_number, n);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\treturn parse_location_path();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tstruct binary_op_t\r\n\t\t{\r\n\t\t\tast_type_t asttype;\r\n\t\t\txpath_value_type rettype;\r\n\t\t\tint precedence;\r\n\r\n\t\t\tbinary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0)\r\n\t\t\t{\r\n\t\t\t}\r\n\r\n\t\t\tbinary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_)\r\n\t\t\t{\r\n\t\t\t}\r\n\r\n\t\t\tstatic binary_op_t parse(xpath_lexer& lexer)\r\n\t\t\t{\r\n\t\t\t\tswitch (lexer.current())\r\n\t\t\t\t{\r\n\t\t\t\tcase lex_string:\r\n\t\t\t\t\tif (lexer.contents() == PUGIXML_TEXT(\"or\"))\r\n\t\t\t\t\t\treturn binary_op_t(ast_op_or, xpath_type_boolean, 1);\r\n\t\t\t\t\telse if (lexer.contents() == PUGIXML_TEXT(\"and\"))\r\n\t\t\t\t\t\treturn binary_op_t(ast_op_and, xpath_type_boolean, 2);\r\n\t\t\t\t\telse if (lexer.contents() == PUGIXML_TEXT(\"div\"))\r\n\t\t\t\t\t\treturn binary_op_t(ast_op_divide, xpath_type_number, 6);\r\n\t\t\t\t\telse if (lexer.contents() == PUGIXML_TEXT(\"mod\"))\r\n\t\t\t\t\t\treturn binary_op_t(ast_op_mod, xpath_type_number, 6);\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\treturn binary_op_t();\r\n\r\n\t\t\t\tcase lex_equal:\r\n\t\t\t\t\treturn binary_op_t(ast_op_equal, xpath_type_boolean, 3);\r\n\r\n\t\t\t\tcase lex_not_equal:\r\n\t\t\t\t\treturn binary_op_t(ast_op_not_equal, xpath_type_boolean, 3);\r\n\r\n\t\t\t\tcase lex_less:\r\n\t\t\t\t\treturn binary_op_t(ast_op_less, xpath_type_boolean, 4);\r\n\r\n\t\t\t\tcase lex_greater:\r\n\t\t\t\t\treturn binary_op_t(ast_op_greater, xpath_type_boolean, 4);\r\n\r\n\t\t\t\tcase lex_less_or_equal:\r\n\t\t\t\t\treturn binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4);\r\n\r\n\t\t\t\tcase lex_greater_or_equal:\r\n\t\t\t\t\treturn binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4);\r\n\r\n\t\t\t\tcase lex_plus:\r\n\t\t\t\t\treturn binary_op_t(ast_op_add, xpath_type_number, 5);\r\n\r\n\t\t\t\tcase lex_minus:\r\n\t\t\t\t\treturn binary_op_t(ast_op_subtract, xpath_type_number, 5);\r\n\r\n\t\t\t\tcase lex_multiply:\r\n\t\t\t\t\treturn binary_op_t(ast_op_multiply, xpath_type_number, 6);\r\n\r\n\t\t\t\tcase lex_union:\r\n\t\t\t\t\treturn binary_op_t(ast_op_union, xpath_type_node_set, 7);\r\n\r\n\t\t\t\tdefault:\r\n\t\t\t\t\treturn binary_op_t();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\txpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit)\r\n\t\t{\r\n\t\t\tbinary_op_t op = binary_op_t::parse(_lexer);\r\n\r\n\t\t\twhile (op.asttype != ast_unknown && op.precedence >= limit)\r\n\t\t\t{\r\n\t\t\t\t_lexer.next();\r\n\r\n\t\t\t\tif (++_depth > xpath_ast_depth_limit)\r\n\t\t\t\t\treturn error_rec();\r\n\r\n\t\t\t\txpath_ast_node* rhs = parse_path_or_unary_expression();\r\n\t\t\t\tif (!rhs) return 0;\r\n\r\n\t\t\t\tbinary_op_t nextop = binary_op_t::parse(_lexer);\r\n\r\n\t\t\t\twhile (nextop.asttype != ast_unknown && nextop.precedence > op.precedence)\r\n\t\t\t\t{\r\n\t\t\t\t\trhs = parse_expression_rec(rhs, nextop.precedence);\r\n\t\t\t\t\tif (!rhs) return 0;\r\n\r\n\t\t\t\t\tnextop = binary_op_t::parse(_lexer);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set))\r\n\t\t\t\t\treturn error(\"Union operator has to be applied to node sets\");\r\n\r\n\t\t\t\tlhs = alloc_node(op.asttype, op.rettype, lhs, rhs);\r\n\t\t\t\tif (!lhs) return 0;\r\n\r\n\t\t\t\top = binary_op_t::parse(_lexer);\r\n\t\t\t}\r\n\r\n\t\t\treturn lhs;\r\n\t\t}\r\n\r\n\t\t// Expr ::= OrExpr\r\n\t\t// OrExpr ::= AndExpr | OrExpr 'or' AndExpr\r\n\t\t// AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr\r\n\t\t// EqualityExpr ::= RelationalExpr\r\n\t\t//\t\t\t\t\t| EqualityExpr '=' RelationalExpr\r\n\t\t//\t\t\t\t\t| EqualityExpr '!=' RelationalExpr\r\n\t\t// RelationalExpr ::= AdditiveExpr\r\n\t\t//\t\t\t\t\t  | RelationalExpr '<' AdditiveExpr\r\n\t\t//\t\t\t\t\t  | RelationalExpr '>' AdditiveExpr\r\n\t\t//\t\t\t\t\t  | RelationalExpr '<=' AdditiveExpr\r\n\t\t//\t\t\t\t\t  | RelationalExpr '>=' AdditiveExpr\r\n\t\t// AdditiveExpr ::= MultiplicativeExpr\r\n\t\t//\t\t\t\t\t| AdditiveExpr '+' MultiplicativeExpr\r\n\t\t//\t\t\t\t\t| AdditiveExpr '-' MultiplicativeExpr\r\n\t\t// MultiplicativeExpr ::= UnaryExpr\r\n\t\t//\t\t\t\t\t\t  | MultiplicativeExpr '*' UnaryExpr\r\n\t\t//\t\t\t\t\t\t  | MultiplicativeExpr 'div' UnaryExpr\r\n\t\t//\t\t\t\t\t\t  | MultiplicativeExpr 'mod' UnaryExpr\r\n\t\txpath_ast_node* parse_expression(int limit = 0)\r\n\t\t{\r\n\t\t\tsize_t old_depth = _depth;\r\n\r\n\t\t\tif (++_depth > xpath_ast_depth_limit)\r\n\t\t\t\treturn error_rec();\r\n\r\n\t\t\txpath_ast_node* n = parse_path_or_unary_expression();\r\n\t\t\tif (!n) return 0;\r\n\r\n\t\t\tn = parse_expression_rec(n, limit);\r\n\r\n\t\t\t_depth = old_depth;\r\n\r\n\t\t\treturn n;\r\n\t\t}\r\n\r\n\t\txpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result), _depth(0)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\txpath_ast_node* parse()\r\n\t\t{\r\n\t\t\txpath_ast_node* n = parse_expression();\r\n\t\t\tif (!n) return 0;\r\n\r\n\t\t\tassert(_depth == 0);\r\n\r\n\t\t\t// check if there are unparsed tokens left\r\n\t\t\tif (_lexer.current() != lex_eof)\r\n\t\t\t\treturn error(\"Incorrect query\");\r\n\r\n\t\t\treturn n;\r\n\t\t}\r\n\r\n\t\tstatic xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result)\r\n\t\t{\r\n\t\t\txpath_parser parser(query, variables, alloc, result);\r\n\r\n\t\t\treturn parser.parse();\r\n\t\t}\r\n\t};\r\n\r\n\tstruct xpath_query_impl\r\n\t{\r\n\t\tstatic xpath_query_impl* create()\r\n\t\t{\r\n\t\t\tvoid* memory = xml_memory::allocate(sizeof(xpath_query_impl));\r\n\t\t\tif (!memory) return 0;\r\n\r\n\t\t\treturn new (memory) xpath_query_impl();\r\n\t\t}\r\n\r\n\t\tstatic void destroy(xpath_query_impl* impl)\r\n\t\t{\r\n\t\t\t// free all allocated pages\r\n\t\t\timpl->alloc.release();\r\n\r\n\t\t\t// free allocator memory (with the first page)\r\n\t\t\txml_memory::deallocate(impl);\r\n\t\t}\r\n\r\n\t\txpath_query_impl(): root(0), alloc(&block, &oom), oom(false)\r\n\t\t{\r\n\t\t\tblock.next = 0;\r\n\t\t\tblock.capacity = sizeof(block.data);\r\n\t\t}\r\n\r\n\t\txpath_ast_node* root;\r\n\t\txpath_allocator alloc;\r\n\t\txpath_memory_block block;\r\n\t\tbool oom;\r\n\t};\r\n\r\n\tPUGI_IMPL_FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl)\r\n\t{\r\n\t\tif (!impl) return 0;\r\n\r\n\t\tif (impl->root->rettype() != xpath_type_node_set)\r\n\t\t{\r\n\t\t#ifdef PUGIXML_NO_EXCEPTIONS\r\n\t\t\treturn 0;\r\n\t\t#else\r\n\t\t\txpath_parse_result res;\r\n\t\t\tres.error = \"Expression does not evaluate to node set\";\r\n\r\n\t\t\tthrow xpath_exception(res);\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\treturn impl->root;\r\n\t}\r\nPUGI_IMPL_NS_END\r\n\r\nnamespace pugi\r\n{\r\n#ifndef PUGIXML_NO_EXCEPTIONS\r\n\tPUGI_IMPL_FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_)\r\n\t{\r\n\t\tassert(_result.error);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char* xpath_exception::what() const throw()\r\n\t{\r\n\t\treturn _result.error;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const xpath_parse_result& xpath_exception::result() const\r\n\t{\r\n\t\treturn _result;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xpath_node::xpath_node()\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node::xpath_node(const xml_node& node_): _node(node_)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xpath_node::node() const\r\n\t{\r\n\t\treturn _attribute ? xml_node() : _node;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_attribute xpath_node::attribute() const\r\n\t{\r\n\t\treturn _attribute;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xml_node xpath_node::parent() const\r\n\t{\r\n\t\treturn _attribute ? _node : _node.parent();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN static void unspecified_bool_xpath_node(xpath_node***)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node::operator xpath_node::unspecified_bool_type() const\r\n\t{\r\n\t\treturn (_node || _attribute) ? unspecified_bool_xpath_node : 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_node::operator!() const\r\n\t{\r\n\t\treturn !(_node || _attribute);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_node::operator==(const xpath_node& n) const\r\n\t{\r\n\t\treturn _node == n._node && _attribute == n._attribute;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_node::operator!=(const xpath_node& n) const\r\n\t{\r\n\t\treturn _node != n._node || _attribute != n._attribute;\r\n\t}\r\n\r\n#ifdef __BORLANDC__\r\n\tPUGI_IMPL_FN bool operator&&(const xpath_node& lhs, bool rhs)\r\n\t{\r\n\t\treturn (bool)lhs && rhs;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool operator||(const xpath_node& lhs, bool rhs)\r\n\t{\r\n\t\treturn (bool)lhs || rhs;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_)\r\n\t{\r\n\t\tassert(begin_ <= end_);\r\n\r\n\t\tsize_t size_ = static_cast<size_t>(end_ - begin_);\r\n\r\n\t\t// use internal buffer for 0 or 1 elements, heap buffer otherwise\r\n\t\txpath_node* storage = (size_ <= 1) ? _storage : static_cast<xpath_node*>(impl::xml_memory::allocate(size_ * sizeof(xpath_node)));\r\n\r\n\t\tif (!storage)\r\n\t\t{\r\n\t\t#ifdef PUGIXML_NO_EXCEPTIONS\r\n\t\t\treturn;\r\n\t\t#else\r\n\t\t\tthrow std::bad_alloc();\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\t// deallocate old buffer\r\n\t\tif (_begin != _storage)\r\n\t\t\timpl::xml_memory::deallocate(_begin);\r\n\r\n\t\t// size check is necessary because for begin_ = end_ = nullptr, memcpy is UB\r\n\t\tif (size_)\r\n\t\t\tmemcpy(storage, begin_, size_ * sizeof(xpath_node));\r\n\r\n\t\t_begin = storage;\r\n\t\t_end = storage + size_;\r\n\t\t_type = type_;\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_MOVE\r\n\tPUGI_IMPL_FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT\r\n\t{\r\n\t\t_type = rhs._type;\r\n\t\t_storage[0] = rhs._storage[0];\r\n\t\t_begin = (rhs._begin == rhs._storage) ? _storage : rhs._begin;\r\n\t\t_end = _begin + (rhs._end - rhs._begin);\r\n\r\n\t\trhs._type = type_unsorted;\r\n\t\trhs._begin = rhs._storage;\r\n\t\trhs._end = rhs._storage;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(_storage), _end(_storage)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(_storage), _end(_storage)\r\n\t{\r\n\t\t_assign(begin_, end_, type_);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node_set::~xpath_node_set()\r\n\t{\r\n\t\tif (_begin != _storage)\r\n\t\t\timpl::xml_memory::deallocate(_begin);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(_storage), _end(_storage)\r\n\t{\r\n\t\t_assign(ns._begin, ns._end, ns._type);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns)\r\n\t{\r\n\t\tif (this == &ns) return *this;\r\n\r\n\t\t_assign(ns._begin, ns._end, ns._type);\r\n\r\n\t\treturn *this;\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_MOVE\r\n\tPUGI_IMPL_FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(_storage), _end(_storage)\r\n\t{\r\n\t\t_move(rhs);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT\r\n\t{\r\n\t\tif (this == &rhs) return *this;\r\n\r\n\t\tif (_begin != _storage)\r\n\t\t\timpl::xml_memory::deallocate(_begin);\r\n\r\n\t\t_move(rhs);\r\n\r\n\t\treturn *this;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xpath_node_set::type_t xpath_node_set::type() const\r\n\t{\r\n\t\treturn _type;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN size_t xpath_node_set::size() const\r\n\t{\r\n\t\treturn _end - _begin;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_node_set::empty() const\r\n\t{\r\n\t\treturn _begin == _end;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const xpath_node& xpath_node_set::operator[](size_t index) const\r\n\t{\r\n\t\tassert(index < size());\r\n\t\treturn _begin[index];\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node_set::const_iterator xpath_node_set::begin() const\r\n\t{\r\n\t\treturn _begin;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node_set::const_iterator xpath_node_set::end() const\r\n\t{\r\n\t\treturn _end;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xpath_node_set::sort(bool reverse)\r\n\t{\r\n\t\t_type = impl::xpath_sort(_begin, _end, _type, reverse);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node xpath_node_set::first() const\r\n\t{\r\n\t\treturn impl::xpath_first(_begin, _end, _type);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_parse_result::xpath_parse_result(): error(\"Internal error\"), offset(0)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_parse_result::operator bool() const\r\n\t{\r\n\t\treturn error == 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char* xpath_parse_result::description() const\r\n\t{\r\n\t\treturn error ? error : \"No error\";\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* xpath_variable::name() const\r\n\t{\r\n\t\tswitch (_type)\r\n\t\t{\r\n\t\tcase xpath_type_node_set:\r\n\t\t\treturn static_cast<const impl::xpath_variable_node_set*>(this)->name;\r\n\r\n\t\tcase xpath_type_number:\r\n\t\t\treturn static_cast<const impl::xpath_variable_number*>(this)->name;\r\n\r\n\t\tcase xpath_type_string:\r\n\t\t\treturn static_cast<const impl::xpath_variable_string*>(this)->name;\r\n\r\n\t\tcase xpath_type_boolean:\r\n\t\t\treturn static_cast<const impl::xpath_variable_boolean*>(this)->name;\r\n\r\n\t\tdefault:\r\n\t\t\tassert(false && \"Invalid variable type\"); // unreachable\r\n\t\t\treturn 0;\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_value_type xpath_variable::type() const\r\n\t{\r\n\t\treturn _type;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_variable::get_boolean() const\r\n\t{\r\n\t\treturn (_type == xpath_type_boolean) ? static_cast<const impl::xpath_variable_boolean*>(this)->value : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN double xpath_variable::get_number() const\r\n\t{\r\n\t\treturn (_type == xpath_type_number) ? static_cast<const impl::xpath_variable_number*>(this)->value : impl::gen_nan();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const char_t* xpath_variable::get_string() const\r\n\t{\r\n\t\tconst char_t* value = (_type == xpath_type_string) ? static_cast<const impl::xpath_variable_string*>(this)->value : 0;\r\n\t\treturn value ? value : PUGIXML_TEXT(\"\");\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const xpath_node_set& xpath_variable::get_node_set() const\r\n\t{\r\n\t\treturn (_type == xpath_type_node_set) ? static_cast<const impl::xpath_variable_node_set*>(this)->value : impl::dummy_node_set;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_variable::set(bool value)\r\n\t{\r\n\t\tif (_type != xpath_type_boolean) return false;\r\n\r\n\t\tstatic_cast<impl::xpath_variable_boolean*>(this)->value = value;\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_variable::set(double value)\r\n\t{\r\n\t\tif (_type != xpath_type_number) return false;\r\n\r\n\t\tstatic_cast<impl::xpath_variable_number*>(this)->value = value;\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_variable::set(const char_t* value)\r\n\t{\r\n\t\tif (_type != xpath_type_string) return false;\r\n\r\n\t\timpl::xpath_variable_string* var = static_cast<impl::xpath_variable_string*>(this);\r\n\r\n\t\t// duplicate string\r\n\t\tsize_t size = (impl::strlength(value) + 1) * sizeof(char_t);\r\n\r\n\t\tchar_t* copy = static_cast<char_t*>(impl::xml_memory::allocate(size));\r\n\t\tif (!copy) return false;\r\n\r\n\t\tmemcpy(copy, value, size);\r\n\r\n\t\t// replace old string\r\n\t\tif (var->value) impl::xml_memory::deallocate(var->value);\r\n\t\tvar->value = copy;\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_variable::set(const xpath_node_set& value)\r\n\t{\r\n\t\tif (_type != xpath_type_node_set) return false;\r\n\r\n\t\tstatic_cast<impl::xpath_variable_node_set*>(this)->value = value;\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_variable_set::xpath_variable_set()\r\n\t{\r\n\t\tfor (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)\r\n\t\t\t_data[i] = 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_variable_set::~xpath_variable_set()\r\n\t{\r\n\t\tfor (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)\r\n\t\t\t_destroy(_data[i]);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs)\r\n\t{\r\n\t\tfor (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)\r\n\t\t\t_data[i] = 0;\r\n\r\n\t\t_assign(rhs);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs)\r\n\t{\r\n\t\tif (this == &rhs) return *this;\r\n\r\n\t\t_assign(rhs);\r\n\r\n\t\treturn *this;\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_MOVE\r\n\tPUGI_IMPL_FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT\r\n\t{\r\n\t\tfor (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)\r\n\t\t{\r\n\t\t\t_data[i] = rhs._data[i];\r\n\t\t\trhs._data[i] = 0;\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT\r\n\t{\r\n\t\tfor (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)\r\n\t\t{\r\n\t\t\t_destroy(_data[i]);\r\n\r\n\t\t\t_data[i] = rhs._data[i];\r\n\t\t\trhs._data[i] = 0;\r\n\t\t}\r\n\r\n\t\treturn *this;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN void xpath_variable_set::_assign(const xpath_variable_set& rhs)\r\n\t{\r\n\t\txpath_variable_set temp;\r\n\r\n\t\tfor (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)\r\n\t\t\tif (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i]))\r\n\t\t\t\treturn;\r\n\r\n\t\t_swap(temp);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xpath_variable_set::_swap(xpath_variable_set& rhs)\r\n\t{\r\n\t\tfor (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)\r\n\t\t{\r\n\t\t\txpath_variable* chain = _data[i];\r\n\r\n\t\t\t_data[i] = rhs._data[i];\r\n\t\t\trhs._data[i] = chain;\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_variable* xpath_variable_set::_find(const char_t* name) const\r\n\t{\r\n\t\tconst size_t hash_size = sizeof(_data) / sizeof(_data[0]);\r\n\t\tsize_t hash = impl::hash_string(name) % hash_size;\r\n\r\n\t\t// look for existing variable\r\n\t\tfor (xpath_variable* var = _data[hash]; var; var = var->_next)\r\n\t\t\tif (impl::strequal(var->name(), name))\r\n\t\t\t\treturn var;\r\n\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result)\r\n\t{\r\n\t\txpath_variable* last = 0;\r\n\r\n\t\twhile (var)\r\n\t\t{\r\n\t\t\t// allocate storage for new variable\r\n\t\t\txpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name());\r\n\t\t\tif (!nvar) return false;\r\n\r\n\t\t\t// link the variable to the result immediately to handle failures gracefully\r\n\t\t\tif (last)\r\n\t\t\t\tlast->_next = nvar;\r\n\t\t\telse\r\n\t\t\t\t*out_result = nvar;\r\n\r\n\t\t\tlast = nvar;\r\n\r\n\t\t\t// copy the value; this can fail due to out-of-memory conditions\r\n\t\t\tif (!impl::copy_xpath_variable(nvar, var)) return false;\r\n\r\n\t\t\tvar = var->_next;\r\n\t\t}\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN void xpath_variable_set::_destroy(xpath_variable* var)\r\n\t{\r\n\t\twhile (var)\r\n\t\t{\r\n\t\t\txpath_variable* next = var->_next;\r\n\r\n\t\t\timpl::delete_xpath_variable(var->_type, var);\r\n\r\n\t\t\tvar = next;\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type)\r\n\t{\r\n\t\tconst size_t hash_size = sizeof(_data) / sizeof(_data[0]);\r\n\t\tsize_t hash = impl::hash_string(name) % hash_size;\r\n\r\n\t\t// look for existing variable\r\n\t\tfor (xpath_variable* var = _data[hash]; var; var = var->_next)\r\n\t\t\tif (impl::strequal(var->name(), name))\r\n\t\t\t\treturn var->type() == type ? var : 0;\r\n\r\n\t\t// add new variable\r\n\t\txpath_variable* result = impl::new_xpath_variable(type, name);\r\n\r\n\t\tif (result)\r\n\t\t{\r\n\t\t\tresult->_next = _data[hash];\r\n\r\n\t\t\t_data[hash] = result;\r\n\t\t}\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, bool value)\r\n\t{\r\n\t\txpath_variable* var = add(name, xpath_type_boolean);\r\n\t\treturn var ? var->set(value) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, double value)\r\n\t{\r\n\t\txpath_variable* var = add(name, xpath_type_number);\r\n\t\treturn var ? var->set(value) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, const char_t* value)\r\n\t{\r\n\t\txpath_variable* var = add(name, xpath_type_string);\r\n\t\treturn var ? var->set(value) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value)\r\n\t{\r\n\t\txpath_variable* var = add(name, xpath_type_node_set);\r\n\t\treturn var ? var->set(value) : false;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_variable* xpath_variable_set::get(const char_t* name)\r\n\t{\r\n\t\treturn _find(name);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const xpath_variable* xpath_variable_set::get(const char_t* name) const\r\n\t{\r\n\t\treturn _find(name);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0)\r\n\t{\r\n\t\timpl::xpath_query_impl* qimpl = impl::xpath_query_impl::create();\r\n\r\n\t\tif (!qimpl)\r\n\t\t{\r\n\t\t#ifdef PUGIXML_NO_EXCEPTIONS\r\n\t\t\t_result.error = \"Out of memory\";\r\n\t\t#else\r\n\t\t\tthrow std::bad_alloc();\r\n\t\t#endif\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tusing impl::auto_deleter; // MSVC7 workaround\r\n\t\t\tauto_deleter<impl::xpath_query_impl> impl(qimpl, impl::xpath_query_impl::destroy);\r\n\r\n\t\t\tqimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result);\r\n\r\n\t\t\tif (qimpl->root)\r\n\t\t\t{\r\n\t\t\t\tqimpl->root->optimize(&qimpl->alloc);\r\n\r\n\t\t\t\t_impl = impl.release();\r\n\t\t\t\t_result.error = 0;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t#ifdef PUGIXML_NO_EXCEPTIONS\r\n\t\t\t\tif (qimpl->oom) _result.error = \"Out of memory\";\r\n\t\t\t#else\r\n\t\t\t\tif (qimpl->oom) throw std::bad_alloc();\r\n\t\t\t\tthrow xpath_exception(_result);\r\n\t\t\t#endif\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_query::xpath_query(): _impl(0)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_query::~xpath_query()\r\n\t{\r\n\t\tif (_impl)\r\n\t\t\timpl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));\r\n\t}\r\n\r\n#ifdef PUGIXML_HAS_MOVE\r\n\tPUGI_IMPL_FN xpath_query::xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT\r\n\t{\r\n\t\t_impl = rhs._impl;\r\n\t\t_result = rhs._result;\r\n\t\trhs._impl = 0;\r\n\t\trhs._result = xpath_parse_result();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_query& xpath_query::operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT\r\n\t{\r\n\t\tif (this == &rhs) return *this;\r\n\r\n\t\tif (_impl)\r\n\t\t\timpl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));\r\n\r\n\t\t_impl = rhs._impl;\r\n\t\t_result = rhs._result;\r\n\t\trhs._impl = 0;\r\n\t\trhs._result = xpath_parse_result();\r\n\r\n\t\treturn *this;\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN xpath_value_type xpath_query::return_type() const\r\n\t{\r\n\t\tif (!_impl) return xpath_type_none;\r\n\r\n\t\treturn static_cast<impl::xpath_query_impl*>(_impl)->root->rettype();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_query::evaluate_boolean(const xpath_node& n) const\r\n\t{\r\n\t\tif (!_impl) return false;\r\n\r\n\t\timpl::xpath_context c(n, 1, 1);\r\n\t\timpl::xpath_stack_data sd;\r\n\r\n\t\tbool r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_boolean(c, sd.stack);\r\n\r\n\t\tif (sd.oom)\r\n\t\t{\r\n\t\t#ifdef PUGIXML_NO_EXCEPTIONS\r\n\t\t\treturn false;\r\n\t\t#else\r\n\t\t\tthrow std::bad_alloc();\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\treturn r;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN double xpath_query::evaluate_number(const xpath_node& n) const\r\n\t{\r\n\t\tif (!_impl) return impl::gen_nan();\r\n\r\n\t\timpl::xpath_context c(n, 1, 1);\r\n\t\timpl::xpath_stack_data sd;\r\n\r\n\t\tdouble r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_number(c, sd.stack);\r\n\r\n\t\tif (sd.oom)\r\n\t\t{\r\n\t\t#ifdef PUGIXML_NO_EXCEPTIONS\r\n\t\t\treturn impl::gen_nan();\r\n\t\t#else\r\n\t\t\tthrow std::bad_alloc();\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\treturn r;\r\n\t}\r\n\r\n#ifndef PUGIXML_NO_STL\r\n\tPUGI_IMPL_FN string_t xpath_query::evaluate_string(const xpath_node& n) const\r\n\t{\r\n\t\tif (!_impl) return string_t();\r\n\r\n\t\timpl::xpath_context c(n, 1, 1);\r\n\t\timpl::xpath_stack_data sd;\r\n\r\n\t\timpl::xpath_string r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_string(c, sd.stack);\r\n\r\n\t\tif (sd.oom)\r\n\t\t{\r\n\t\t#ifdef PUGIXML_NO_EXCEPTIONS\r\n\t\t\treturn string_t();\r\n\t\t#else\r\n\t\t\tthrow std::bad_alloc();\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\treturn string_t(r.c_str(), r.length());\r\n\t}\r\n#endif\r\n\r\n\tPUGI_IMPL_FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const\r\n\t{\r\n\t\timpl::xpath_context c(n, 1, 1);\r\n\t\timpl::xpath_stack_data sd;\r\n\r\n\t\timpl::xpath_string r = _impl ? static_cast<impl::xpath_query_impl*>(_impl)->root->eval_string(c, sd.stack) : impl::xpath_string();\r\n\r\n\t\tif (sd.oom)\r\n\t\t{\r\n\t\t#ifdef PUGIXML_NO_EXCEPTIONS\r\n\t\t\tr = impl::xpath_string();\r\n\t\t#else\r\n\t\t\tthrow std::bad_alloc();\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\tsize_t full_size = r.length() + 1;\r\n\r\n\t\tif (capacity > 0)\r\n\t\t{\r\n\t\t\tsize_t size = (full_size < capacity) ? full_size : capacity;\r\n\t\t\tassert(size > 0);\r\n\r\n\t\t\tmemcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t));\r\n\t\t\tbuffer[size - 1] = 0;\r\n\t\t}\r\n\r\n\t\treturn full_size;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const\r\n\t{\r\n\t\timpl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));\r\n\t\tif (!root) return xpath_node_set();\r\n\r\n\t\timpl::xpath_context c(n, 1, 1);\r\n\t\timpl::xpath_stack_data sd;\r\n\r\n\t\timpl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all);\r\n\r\n\t\tif (sd.oom)\r\n\t\t{\r\n\t\t#ifdef PUGIXML_NO_EXCEPTIONS\r\n\t\t\treturn xpath_node_set();\r\n\t\t#else\r\n\t\t\tthrow std::bad_alloc();\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\treturn xpath_node_set(r.begin(), r.end(), r.type());\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const\r\n\t{\r\n\t\timpl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));\r\n\t\tif (!root) return xpath_node();\r\n\r\n\t\timpl::xpath_context c(n, 1, 1);\r\n\t\timpl::xpath_stack_data sd;\r\n\r\n\t\timpl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first);\r\n\r\n\t\tif (sd.oom)\r\n\t\t{\r\n\t\t#ifdef PUGIXML_NO_EXCEPTIONS\r\n\t\t\treturn xpath_node();\r\n\t\t#else\r\n\t\t\tthrow std::bad_alloc();\r\n\t\t#endif\r\n\t\t}\r\n\r\n\t\treturn r.first();\r\n\t}\r\n\r\n\tPUGI_IMPL_FN const xpath_parse_result& xpath_query::result() const\r\n\t{\r\n\t\treturn _result;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN static void unspecified_bool_xpath_query(xpath_query***)\r\n\t{\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_query::operator xpath_query::unspecified_bool_type() const\r\n\t{\r\n\t\treturn _impl ? unspecified_bool_xpath_query : 0;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN bool xpath_query::operator!() const\r\n\t{\r\n\t\treturn !_impl;\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const\r\n\t{\r\n\t\txpath_query q(query, variables);\r\n\t\treturn q.evaluate_node(*this);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node xml_node::select_node(const xpath_query& query) const\r\n\t{\r\n\t\treturn query.evaluate_node(*this);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const\r\n\t{\r\n\t\txpath_query q(query, variables);\r\n\t\treturn q.evaluate_node_set(*this);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const\r\n\t{\r\n\t\treturn query.evaluate_node_set(*this);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const\r\n\t{\r\n\t\txpath_query q(query, variables);\r\n\t\treturn q.evaluate_node(*this);\r\n\t}\r\n\r\n\tPUGI_IMPL_FN xpath_node xml_node::select_single_node(const xpath_query& query) const\r\n\t{\r\n\t\treturn query.evaluate_node(*this);\r\n\t}\r\n}\r\n\r\n#endif\r\n\r\n#ifdef __BORLANDC__\r\n#\tpragma option pop\r\n#endif\r\n\r\n// Intel C++ does not properly keep warning state for function templates,\r\n// so popping warning state at the end of translation unit leads to warnings in the middle.\r\n#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)\r\n#\tpragma warning(pop)\r\n#endif\r\n\r\n#if defined(_MSC_VER) && defined(__c2__)\r\n#\tpragma clang diagnostic pop\r\n#endif\r\n\r\n// Undefine all local macros (makes sure we're not leaking macros in header-only mode)\r\n#undef PUGI_IMPL_NO_INLINE\r\n#undef PUGI_IMPL_UNLIKELY\r\n#undef PUGI_IMPL_STATIC_ASSERT\r\n#undef PUGI_IMPL_DMC_VOLATILE\r\n#undef PUGI_IMPL_UNSIGNED_OVERFLOW\r\n#undef PUGI_IMPL_MSVC_CRT_VERSION\r\n#undef PUGI_IMPL_SNPRINTF\r\n#undef PUGI_IMPL_NS_BEGIN\r\n#undef PUGI_IMPL_NS_END\r\n#undef PUGI_IMPL_FN\r\n#undef PUGI_IMPL_FN_NO_INLINE\r\n#undef PUGI_IMPL_GETHEADER_IMPL\r\n#undef PUGI_IMPL_GETPAGE_IMPL\r\n#undef PUGI_IMPL_GETPAGE\r\n#undef PUGI_IMPL_NODETYPE\r\n#undef PUGI_IMPL_IS_CHARTYPE_IMPL\r\n#undef PUGI_IMPL_IS_CHARTYPE\r\n#undef PUGI_IMPL_IS_CHARTYPEX\r\n#undef PUGI_IMPL_ENDSWITH\r\n#undef PUGI_IMPL_SKIPWS\r\n#undef PUGI_IMPL_OPTSET\r\n#undef PUGI_IMPL_PUSHNODE\r\n#undef PUGI_IMPL_POPNODE\r\n#undef PUGI_IMPL_SCANFOR\r\n#undef PUGI_IMPL_SCANWHILE\r\n#undef PUGI_IMPL_SCANWHILE_UNROLL\r\n#undef PUGI_IMPL_ENDSEG\r\n#undef PUGI_IMPL_THROW_ERROR\r\n#undef PUGI_IMPL_CHECK_ERROR\r\n\r\n#endif\r\n\r\n/**\r\n * Copyright (c) 2006-2023 Arseny Kapoulkine\r\n *\r\n * Permission is hereby granted, free of charge, to any person\r\n * obtaining a copy of this software and associated documentation\r\n * files (the \"Software\"), to deal in the Software without\r\n * restriction, including without limitation the rights to use,\r\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n * copies of the Software, and to permit persons to whom the\r\n * Software is furnished to do so, subject to the following\r\n * conditions:\r\n *\r\n * The above copyright notice and this permission notice shall be\r\n * included in all copies or substantial portions of the Software.\r\n *\r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\r\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\r\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\r\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\r\n * OTHER DEALINGS IN THE SOFTWARE.\r\n */\r\n"
  },
  {
    "path": "third-party/pugixml/src/pugixml.hpp",
    "content": "/**\r\n * pugixml parser - version 1.14\r\n * --------------------------------------------------------\r\n * Copyright (C) 2006-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)\r\n * Report bugs and download new versions at https://pugixml.org/\r\n *\r\n * This library is distributed under the MIT License. See notice at the end\r\n * of this file.\r\n *\r\n * This work is based on the pugxml parser, which is:\r\n * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)\r\n */\r\n\r\n// Define version macro; evaluates to major * 1000 + minor * 10 + patch so that it's safe to use in less-than comparisons\r\n// Note: pugixml used major * 100 + minor * 10 + patch format up until 1.9 (which had version identifier 190); starting from pugixml 1.10, the minor version number is two digits\r\n#ifndef PUGIXML_VERSION\r\n#\tdefine PUGIXML_VERSION 1140 // 1.14\r\n#endif\r\n\r\n// Include user configuration file (this can define various configuration macros)\r\n#include \"pugiconfig.hpp\"\r\n\r\n#ifndef HEADER_PUGIXML_HPP\r\n#define HEADER_PUGIXML_HPP\r\n\r\n// Include stddef.h for size_t and ptrdiff_t\r\n#include <stddef.h>\r\n\r\n// Include exception header for XPath\r\n#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS)\r\n#\tinclude <exception>\r\n#endif\r\n\r\n// Include STL headers\r\n#ifndef PUGIXML_NO_STL\r\n#\tinclude <iterator>\r\n#\tinclude <iosfwd>\r\n#\tinclude <string>\r\n#endif\r\n\r\n// Macro for deprecated features\r\n#ifndef PUGIXML_DEPRECATED\r\n#\tif defined(__GNUC__)\r\n#\t\tdefine PUGIXML_DEPRECATED __attribute__((deprecated))\r\n#\telif defined(_MSC_VER) && _MSC_VER >= 1300\r\n#\t\tdefine PUGIXML_DEPRECATED __declspec(deprecated)\r\n#\telse\r\n#\t\tdefine PUGIXML_DEPRECATED\r\n#\tendif\r\n#endif\r\n\r\n// If no API is defined, assume default\r\n#ifndef PUGIXML_API\r\n#\tdefine PUGIXML_API\r\n#endif\r\n\r\n// If no API for classes is defined, assume default\r\n#ifndef PUGIXML_CLASS\r\n#\tdefine PUGIXML_CLASS PUGIXML_API\r\n#endif\r\n\r\n// If no API for functions is defined, assume default\r\n#ifndef PUGIXML_FUNCTION\r\n#\tdefine PUGIXML_FUNCTION PUGIXML_API\r\n#endif\r\n\r\n// If the platform is known to have long long support, enable long long functions\r\n#ifndef PUGIXML_HAS_LONG_LONG\r\n#\tif __cplusplus >= 201103\r\n#\t\tdefine PUGIXML_HAS_LONG_LONG\r\n#\telif defined(_MSC_VER) && _MSC_VER >= 1400\r\n#\t\tdefine PUGIXML_HAS_LONG_LONG\r\n#\tendif\r\n#endif\r\n\r\n// If the platform is known to have move semantics support, compile move ctor/operator implementation\r\n#ifndef PUGIXML_HAS_MOVE\r\n#\tif __cplusplus >= 201103\r\n#\t\tdefine PUGIXML_HAS_MOVE\r\n#\telif defined(_MSC_VER) && _MSC_VER >= 1600\r\n#\t\tdefine PUGIXML_HAS_MOVE\r\n#\tendif\r\n#endif\r\n\r\n// If C++ is 2011 or higher, add 'noexcept' specifiers\r\n#ifndef PUGIXML_NOEXCEPT\r\n#\tif __cplusplus >= 201103\r\n#\t\tdefine PUGIXML_NOEXCEPT noexcept\r\n#\telif defined(_MSC_VER) && _MSC_VER >= 1900\r\n#\t\tdefine PUGIXML_NOEXCEPT noexcept\r\n#\telse\r\n#\t\tdefine PUGIXML_NOEXCEPT\r\n#\tendif\r\n#endif\r\n\r\n// Some functions can not be noexcept in compact mode\r\n#ifdef PUGIXML_COMPACT\r\n#\tdefine PUGIXML_NOEXCEPT_IF_NOT_COMPACT\r\n#else\r\n#\tdefine PUGIXML_NOEXCEPT_IF_NOT_COMPACT PUGIXML_NOEXCEPT\r\n#endif\r\n\r\n// If C++ is 2011 or higher, add 'override' qualifiers\r\n#ifndef PUGIXML_OVERRIDE\r\n#\tif __cplusplus >= 201103\r\n#\t\tdefine PUGIXML_OVERRIDE override\r\n#\telif defined(_MSC_VER) && _MSC_VER >= 1700\r\n#\t\tdefine PUGIXML_OVERRIDE override\r\n#\telse\r\n#\t\tdefine PUGIXML_OVERRIDE\r\n#\tendif\r\n#endif\r\n\r\n// If C++ is 2011 or higher, use 'nullptr'\r\n#ifndef PUGIXML_NULL\r\n#\tif __cplusplus >= 201103\r\n#\t\tdefine PUGIXML_NULL nullptr\r\n#\telif defined(_MSC_VER) && _MSC_VER >= 1600\r\n#\t\tdefine PUGIXML_NULL nullptr\r\n#\telse\r\n#\t\tdefine PUGIXML_NULL 0\r\n#\tendif\r\n#endif\r\n\r\n// Character interface macros\r\n#ifdef PUGIXML_WCHAR_MODE\r\n#\tdefine PUGIXML_TEXT(t) L ## t\r\n#\tdefine PUGIXML_CHAR wchar_t\r\n#else\r\n#\tdefine PUGIXML_TEXT(t) t\r\n#\tdefine PUGIXML_CHAR char\r\n#endif\r\n\r\nnamespace pugi\r\n{\r\n\t// Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE\r\n\ttypedef PUGIXML_CHAR char_t;\r\n\r\n#ifndef PUGIXML_NO_STL\r\n\t// String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE\r\n\ttypedef std::basic_string<PUGIXML_CHAR, std::char_traits<PUGIXML_CHAR>, std::allocator<PUGIXML_CHAR> > string_t;\r\n#endif\r\n}\r\n\r\n// The PugiXML namespace\r\nnamespace pugi\r\n{\r\n\t// Tree node types\r\n\tenum xml_node_type\r\n\t{\r\n\t\tnode_null,\t\t\t// Empty (null) node handle\r\n\t\tnode_document,\t\t// A document tree's absolute root\r\n\t\tnode_element,\t\t// Element tag, i.e. '<node/>'\r\n\t\tnode_pcdata,\t\t// Plain character data, i.e. 'text'\r\n\t\tnode_cdata,\t\t\t// Character data, i.e. '<![CDATA[text]]>'\r\n\t\tnode_comment,\t\t// Comment tag, i.e. '<!-- text -->'\r\n\t\tnode_pi,\t\t\t// Processing instruction, i.e. '<?name?>'\r\n\t\tnode_declaration,\t// Document declaration, i.e. '<?xml version=\"1.0\"?>'\r\n\t\tnode_doctype\t\t// Document type declaration, i.e. '<!DOCTYPE doc>'\r\n\t};\r\n\r\n\t// Parsing options\r\n\r\n\t// Minimal parsing mode (equivalent to turning all other flags off).\r\n\t// Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed.\r\n\tconst unsigned int parse_minimal = 0x0000;\r\n\r\n\t// This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default.\r\n\tconst unsigned int parse_pi = 0x0001;\r\n\r\n\t// This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default.\r\n\tconst unsigned int parse_comments = 0x0002;\r\n\r\n\t// This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default.\r\n\tconst unsigned int parse_cdata = 0x0004;\r\n\r\n\t// This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree.\r\n\t// This flag is off by default; turning it on usually results in slower parsing and more memory consumption.\r\n\tconst unsigned int parse_ws_pcdata = 0x0008;\r\n\r\n\t// This flag determines if character and entity references are expanded during parsing. This flag is on by default.\r\n\tconst unsigned int parse_escapes = 0x0010;\r\n\r\n\t// This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default.\r\n\tconst unsigned int parse_eol = 0x0020;\r\n\r\n\t// This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default.\r\n\tconst unsigned int parse_wconv_attribute = 0x0040;\r\n\r\n\t// This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default.\r\n\tconst unsigned int parse_wnorm_attribute = 0x0080;\r\n\r\n\t// This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default.\r\n\tconst unsigned int parse_declaration = 0x0100;\r\n\r\n\t// This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default.\r\n\tconst unsigned int parse_doctype = 0x0200;\r\n\r\n\t// This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only\r\n\t// of whitespace is added to the DOM tree.\r\n\t// This flag is off by default; turning it on may result in slower parsing and more memory consumption.\r\n\tconst unsigned int parse_ws_pcdata_single = 0x0400;\r\n\r\n\t// This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default.\r\n\tconst unsigned int parse_trim_pcdata = 0x0800;\r\n\r\n\t// This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document\r\n\t// is a valid document. This flag is off by default.\r\n\tconst unsigned int parse_fragment = 0x1000;\r\n\r\n\t// This flag determines if plain character data is be stored in the parent element's value. This significantly changes the structure of\r\n\t// the document; this flag is only recommended for parsing documents with many PCDATA nodes in memory-constrained environments.\r\n\t// This flag is off by default.\r\n\tconst unsigned int parse_embed_pcdata = 0x2000;\r\n\t\r\n\t// This flag determines whether determines whether the the two pcdata should be merged or not, if no intermediatory data are parsed in the document.\r\n\t// This flag is off by default.\r\n\tconst unsigned int parse_merge_pcdata = 0x4000;\r\n\r\n\t// The default parsing mode.\r\n\t// Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded,\r\n\t// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.\r\n\tconst unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol;\r\n\r\n\t// The full parsing mode.\r\n\t// Nodes of all types are added to the DOM tree, character/reference entities are expanded,\r\n\t// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.\r\n\tconst unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype;\r\n\r\n\t// These flags determine the encoding of input data for XML document\r\n\tenum xml_encoding\r\n\t{\r\n\t\tencoding_auto,\t\t// Auto-detect input encoding using BOM or < / <? detection; use UTF8 if BOM is not found\r\n\t\tencoding_utf8,\t\t// UTF8 encoding\r\n\t\tencoding_utf16_le,\t// Little-endian UTF16\r\n\t\tencoding_utf16_be,\t// Big-endian UTF16\r\n\t\tencoding_utf16,\t\t// UTF16 with native endianness\r\n\t\tencoding_utf32_le,\t// Little-endian UTF32\r\n\t\tencoding_utf32_be,\t// Big-endian UTF32\r\n\t\tencoding_utf32,\t\t// UTF32 with native endianness\r\n\t\tencoding_wchar,\t\t// The same encoding wchar_t has (either UTF16 or UTF32)\r\n\t\tencoding_latin1\r\n\t};\r\n\r\n\t// Formatting flags\r\n\r\n\t// Indent the nodes that are written to output stream with as many indentation strings as deep the node is in DOM tree. This flag is on by default.\r\n\tconst unsigned int format_indent = 0x01;\r\n\r\n\t// Write encoding-specific BOM to the output stream. This flag is off by default.\r\n\tconst unsigned int format_write_bom = 0x02;\r\n\r\n\t// Use raw output mode (no indentation and no line breaks are written). This flag is off by default.\r\n\tconst unsigned int format_raw = 0x04;\r\n\r\n\t// Omit default XML declaration even if there is no declaration in the document. This flag is off by default.\r\n\tconst unsigned int format_no_declaration = 0x08;\r\n\r\n\t// Don't escape attribute values and PCDATA contents. This flag is off by default.\r\n\tconst unsigned int format_no_escapes = 0x10;\r\n\r\n\t// Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default.\r\n\tconst unsigned int format_save_file_text = 0x20;\r\n\r\n\t// Write every attribute on a new line with appropriate indentation. This flag is off by default.\r\n\tconst unsigned int format_indent_attributes = 0x40;\r\n\r\n\t// Don't output empty element tags, instead writing an explicit start and end tag even if there are no children. This flag is off by default.\r\n\tconst unsigned int format_no_empty_element_tags = 0x80;\r\n\r\n\t// Skip characters belonging to range [0; 32) instead of \"&#xNN;\" encoding. This flag is off by default.\r\n\tconst unsigned int format_skip_control_chars = 0x100;\r\n\r\n\t// Use single quotes ' instead of double quotes \" for enclosing attribute values. This flag is off by default.\r\n\tconst unsigned int format_attribute_single_quote = 0x200;\r\n\r\n\t// The default set of formatting flags.\r\n\t// Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.\r\n\tconst unsigned int format_default = format_indent;\r\n\r\n\tconst int default_double_precision = 17;\r\n\tconst int default_float_precision = 9;\r\n\r\n\t// Forward declarations\r\n\tstruct xml_attribute_struct;\r\n\tstruct xml_node_struct;\r\n\r\n\tclass xml_node_iterator;\r\n\tclass xml_attribute_iterator;\r\n\tclass xml_named_node_iterator;\r\n\r\n\tclass xml_tree_walker;\r\n\r\n\tstruct xml_parse_result;\r\n\r\n\tclass xml_node;\r\n\r\n\tclass xml_text;\r\n\r\n\t#ifndef PUGIXML_NO_XPATH\r\n\tclass xpath_node;\r\n\tclass xpath_node_set;\r\n\tclass xpath_query;\r\n\tclass xpath_variable_set;\r\n\t#endif\r\n\r\n\t// Range-based for loop support\r\n\ttemplate <typename It> class xml_object_range\r\n\t{\r\n\tpublic:\r\n\t\ttypedef It const_iterator;\r\n\t\ttypedef It iterator;\r\n\r\n\t\txml_object_range(It b, It e): _begin(b), _end(e)\r\n\t\t{\r\n\t\t}\r\n\r\n\t\tIt begin() const { return _begin; }\r\n\t\tIt end() const { return _end; }\r\n\r\n\t\tbool empty() const { return _begin == _end; }\r\n\r\n\tprivate:\r\n\t\tIt _begin, _end;\r\n\t};\r\n\r\n\t// Writer interface for node printing (see xml_node::print)\r\n\tclass PUGIXML_CLASS xml_writer\r\n\t{\r\n\tpublic:\r\n\t\tvirtual ~xml_writer();\r\n\r\n\t\t// Write memory chunk into stream/file/whatever\r\n\t\tvirtual void write(const void* data, size_t size) = 0;\r\n\t};\r\n\r\n\t// xml_writer implementation for FILE*\r\n\tclass PUGIXML_CLASS xml_writer_file: public xml_writer\r\n\t{\r\n\tpublic:\r\n\t\t// Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio\r\n\t\txml_writer_file(void* file);\r\n\r\n\t\tvirtual void write(const void* data, size_t size) PUGIXML_OVERRIDE;\r\n\r\n\tprivate:\r\n\t\tvoid* file;\r\n\t};\r\n\r\n\t#ifndef PUGIXML_NO_STL\r\n\t// xml_writer implementation for streams\r\n\tclass PUGIXML_CLASS xml_writer_stream: public xml_writer\r\n\t{\r\n\tpublic:\r\n\t\t// Construct writer from an output stream object\r\n\t\txml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream);\r\n\t\txml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream);\r\n\r\n\t\tvirtual void write(const void* data, size_t size) PUGIXML_OVERRIDE;\r\n\r\n\tprivate:\r\n\t\tstd::basic_ostream<char, std::char_traits<char> >* narrow_stream;\r\n\t\tstd::basic_ostream<wchar_t, std::char_traits<wchar_t> >* wide_stream;\r\n\t};\r\n\t#endif\r\n\r\n\t// A light-weight handle for manipulating attributes in DOM tree\r\n\tclass PUGIXML_CLASS xml_attribute\r\n\t{\r\n\t\tfriend class xml_attribute_iterator;\r\n\t\tfriend class xml_node;\r\n\r\n\tprivate:\r\n\t\txml_attribute_struct* _attr;\r\n\r\n\t\ttypedef void (*unspecified_bool_type)(xml_attribute***);\r\n\r\n\tpublic:\r\n\t\t// Default constructor. Constructs an empty attribute.\r\n\t\txml_attribute();\r\n\r\n\t\t// Constructs attribute from internal pointer\r\n\t\texplicit xml_attribute(xml_attribute_struct* attr);\r\n\r\n\t\t// Safe bool conversion operator\r\n\t\toperator unspecified_bool_type() const;\r\n\r\n\t\t// Borland C++ workaround\r\n\t\tbool operator!() const;\r\n\r\n\t\t// Comparison operators (compares wrapped attribute pointers)\r\n\t\tbool operator==(const xml_attribute& r) const;\r\n\t\tbool operator!=(const xml_attribute& r) const;\r\n\t\tbool operator<(const xml_attribute& r) const;\r\n\t\tbool operator>(const xml_attribute& r) const;\r\n\t\tbool operator<=(const xml_attribute& r) const;\r\n\t\tbool operator>=(const xml_attribute& r) const;\r\n\r\n\t\t// Check if attribute is empty\r\n\t\tbool empty() const;\r\n\r\n\t\t// Get attribute name/value, or \"\" if attribute is empty\r\n\t\tconst char_t* name() const;\r\n\t\tconst char_t* value() const;\r\n\r\n\t\t// Get attribute value, or the default value if attribute is empty\r\n\t\tconst char_t* as_string(const char_t* def = PUGIXML_TEXT(\"\")) const;\r\n\r\n\t\t// Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty\r\n\t\tint as_int(int def = 0) const;\r\n\t\tunsigned int as_uint(unsigned int def = 0) const;\r\n\t\tdouble as_double(double def = 0) const;\r\n\t\tfloat as_float(float def = 0) const;\r\n\r\n\t#ifdef PUGIXML_HAS_LONG_LONG\r\n\t\tlong long as_llong(long long def = 0) const;\r\n\t\tunsigned long long as_ullong(unsigned long long def = 0) const;\r\n\t#endif\r\n\r\n\t\t// Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty\r\n\t\tbool as_bool(bool def = false) const;\r\n\r\n\t\t// Set attribute name/value (returns false if attribute is empty or there is not enough memory)\r\n\t\tbool set_name(const char_t* rhs);\r\n\t\tbool set_name(const char_t* rhs, size_t size);\r\n\t\tbool set_value(const char_t* rhs);\r\n\t\tbool set_value(const char_t* rhs, size_t size);\r\n\r\n\t\t// Set attribute value with type conversion (numbers are converted to strings, boolean is converted to \"true\"/\"false\")\r\n\t\tbool set_value(int rhs);\r\n\t\tbool set_value(unsigned int rhs);\r\n\t\tbool set_value(long rhs);\r\n\t\tbool set_value(unsigned long rhs);\r\n\t\tbool set_value(double rhs);\r\n\t\tbool set_value(double rhs, int precision);\r\n\t\tbool set_value(float rhs);\r\n\t\tbool set_value(float rhs, int precision);\r\n\t\tbool set_value(bool rhs);\r\n\r\n\t#ifdef PUGIXML_HAS_LONG_LONG\r\n\t\tbool set_value(long long rhs);\r\n\t\tbool set_value(unsigned long long rhs);\r\n\t#endif\r\n\r\n\t\t// Set attribute value (equivalent to set_value without error checking)\r\n\t\txml_attribute& operator=(const char_t* rhs);\r\n\t\txml_attribute& operator=(int rhs);\r\n\t\txml_attribute& operator=(unsigned int rhs);\r\n\t\txml_attribute& operator=(long rhs);\r\n\t\txml_attribute& operator=(unsigned long rhs);\r\n\t\txml_attribute& operator=(double rhs);\r\n\t\txml_attribute& operator=(float rhs);\r\n\t\txml_attribute& operator=(bool rhs);\r\n\r\n\t#ifdef PUGIXML_HAS_LONG_LONG\r\n\t\txml_attribute& operator=(long long rhs);\r\n\t\txml_attribute& operator=(unsigned long long rhs);\r\n\t#endif\r\n\r\n\t\t// Get next/previous attribute in the attribute list of the parent node\r\n\t\txml_attribute next_attribute() const;\r\n\t\txml_attribute previous_attribute() const;\r\n\r\n\t\t// Get hash value (unique for handles to the same object)\r\n\t\tsize_t hash_value() const;\r\n\r\n\t\t// Get internal pointer\r\n\t\txml_attribute_struct* internal_object() const;\r\n\t};\r\n\r\n#ifdef __BORLANDC__\r\n\t// Borland C++ workaround\r\n\tbool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs);\r\n\tbool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs);\r\n#endif\r\n\r\n\t// A light-weight handle for manipulating nodes in DOM tree\r\n\tclass PUGIXML_CLASS xml_node\r\n\t{\r\n\t\tfriend class xml_attribute_iterator;\r\n\t\tfriend class xml_node_iterator;\r\n\t\tfriend class xml_named_node_iterator;\r\n\r\n\tprotected:\r\n\t\txml_node_struct* _root;\r\n\r\n\t\ttypedef void (*unspecified_bool_type)(xml_node***);\r\n\r\n\tpublic:\r\n\t\t// Default constructor. Constructs an empty node.\r\n\t\txml_node();\r\n\r\n\t\t// Constructs node from internal pointer\r\n\t\texplicit xml_node(xml_node_struct* p);\r\n\r\n\t\t// Safe bool conversion operator\r\n\t\toperator unspecified_bool_type() const;\r\n\r\n\t\t// Borland C++ workaround\r\n\t\tbool operator!() const;\r\n\r\n\t\t// Comparison operators (compares wrapped node pointers)\r\n\t\tbool operator==(const xml_node& r) const;\r\n\t\tbool operator!=(const xml_node& r) const;\r\n\t\tbool operator<(const xml_node& r) const;\r\n\t\tbool operator>(const xml_node& r) const;\r\n\t\tbool operator<=(const xml_node& r) const;\r\n\t\tbool operator>=(const xml_node& r) const;\r\n\r\n\t\t// Check if node is empty.\r\n\t\tbool empty() const;\r\n\r\n\t\t// Get node type\r\n\t\txml_node_type type() const;\r\n\r\n\t\t// Get node name, or \"\" if node is empty or it has no name\r\n\t\tconst char_t* name() const;\r\n\r\n\t\t// Get node value, or \"\" if node is empty or it has no value\r\n\t\t// Note: For <node>text</node> node.value() does not return \"text\"! Use child_value() or text() methods to access text inside nodes.\r\n\t\tconst char_t* value() const;\r\n\r\n\t\t// Get attribute list\r\n\t\txml_attribute first_attribute() const;\r\n\t\txml_attribute last_attribute() const;\r\n\r\n\t\t// Get children list\r\n\t\txml_node first_child() const;\r\n\t\txml_node last_child() const;\r\n\r\n\t\t// Get next/previous sibling in the children list of the parent node\r\n\t\txml_node next_sibling() const;\r\n\t\txml_node previous_sibling() const;\r\n\r\n\t\t// Get parent node\r\n\t\txml_node parent() const;\r\n\r\n\t\t// Get root of DOM tree this node belongs to\r\n\t\txml_node root() const;\r\n\r\n\t\t// Get text object for the current node\r\n\t\txml_text text() const;\r\n\r\n\t\t// Get child, attribute or next/previous sibling with the specified name\r\n\t\txml_node child(const char_t* name) const;\r\n\t\txml_attribute attribute(const char_t* name) const;\r\n\t\txml_node next_sibling(const char_t* name) const;\r\n\t\txml_node previous_sibling(const char_t* name) const;\r\n\r\n\t\t// Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast)\r\n\t\txml_attribute attribute(const char_t* name, xml_attribute& hint) const;\r\n\r\n\t\t// Get child value of current node; that is, value of the first child node of type PCDATA/CDATA\r\n\t\tconst char_t* child_value() const;\r\n\r\n\t\t// Get child value of child with specified name. Equivalent to child(name).child_value().\r\n\t\tconst char_t* child_value(const char_t* name) const;\r\n\r\n\t\t// Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value)\r\n\t\tbool set_name(const char_t* rhs);\r\n\t\tbool set_name(const char_t* rhs, size_t size);\r\n\t\tbool set_value(const char_t* rhs);\r\n\t\tbool set_value(const char_t* rhs, size_t size);\r\n\r\n\t\t// Add attribute with specified name. Returns added attribute, or empty attribute on errors.\r\n\t\txml_attribute append_attribute(const char_t* name);\r\n\t\txml_attribute prepend_attribute(const char_t* name);\r\n\t\txml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr);\r\n\t\txml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr);\r\n\r\n\t\t// Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors.\r\n\t\txml_attribute append_copy(const xml_attribute& proto);\r\n\t\txml_attribute prepend_copy(const xml_attribute& proto);\r\n\t\txml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr);\r\n\t\txml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr);\r\n\r\n\t\t// Add child node with specified type. Returns added node, or empty node on errors.\r\n\t\txml_node append_child(xml_node_type type = node_element);\r\n\t\txml_node prepend_child(xml_node_type type = node_element);\r\n\t\txml_node insert_child_after(xml_node_type type, const xml_node& node);\r\n\t\txml_node insert_child_before(xml_node_type type, const xml_node& node);\r\n\r\n\t\t// Add child element with specified name. Returns added node, or empty node on errors.\r\n\t\txml_node append_child(const char_t* name);\r\n\t\txml_node prepend_child(const char_t* name);\r\n\t\txml_node insert_child_after(const char_t* name, const xml_node& node);\r\n\t\txml_node insert_child_before(const char_t* name, const xml_node& node);\r\n\r\n\t\t// Add a copy of the specified node as a child. Returns added node, or empty node on errors.\r\n\t\txml_node append_copy(const xml_node& proto);\r\n\t\txml_node prepend_copy(const xml_node& proto);\r\n\t\txml_node insert_copy_after(const xml_node& proto, const xml_node& node);\r\n\t\txml_node insert_copy_before(const xml_node& proto, const xml_node& node);\r\n\r\n\t\t// Move the specified node to become a child of this node. Returns moved node, or empty node on errors.\r\n\t\txml_node append_move(const xml_node& moved);\r\n\t\txml_node prepend_move(const xml_node& moved);\r\n\t\txml_node insert_move_after(const xml_node& moved, const xml_node& node);\r\n\t\txml_node insert_move_before(const xml_node& moved, const xml_node& node);\r\n\r\n\t\t// Remove specified attribute\r\n\t\tbool remove_attribute(const xml_attribute& a);\r\n\t\tbool remove_attribute(const char_t* name);\r\n\r\n\t\t// Remove all attributes\r\n\t\tbool remove_attributes();\r\n\r\n\t\t// Remove specified child\r\n\t\tbool remove_child(const xml_node& n);\r\n\t\tbool remove_child(const char_t* name);\r\n\r\n\t\t// Remove all children\r\n\t\tbool remove_children();\r\n\r\n\t\t// Parses buffer as an XML document fragment and appends all nodes as children of the current node.\r\n\t\t// Copies/converts the buffer, so it may be deleted or changed after the function returns.\r\n\t\t// Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory.\r\n\t\txml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);\r\n\r\n\t\t// Find attribute using predicate. Returns first attribute for which predicate returned true.\r\n\t\ttemplate <typename Predicate> xml_attribute find_attribute(Predicate pred) const\r\n\t\t{\r\n\t\t\tif (!_root) return xml_attribute();\r\n\r\n\t\t\tfor (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute())\r\n\t\t\t\tif (pred(attrib))\r\n\t\t\t\t\treturn attrib;\r\n\r\n\t\t\treturn xml_attribute();\r\n\t\t}\r\n\r\n\t\t// Find child node using predicate. Returns first child for which predicate returned true.\r\n\t\ttemplate <typename Predicate> xml_node find_child(Predicate pred) const\r\n\t\t{\r\n\t\t\tif (!_root) return xml_node();\r\n\r\n\t\t\tfor (xml_node node = first_child(); node; node = node.next_sibling())\r\n\t\t\t\tif (pred(node))\r\n\t\t\t\t\treturn node;\r\n\r\n\t\t\treturn xml_node();\r\n\t\t}\r\n\r\n\t\t// Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true.\r\n\t\ttemplate <typename Predicate> xml_node find_node(Predicate pred) const\r\n\t\t{\r\n\t\t\tif (!_root) return xml_node();\r\n\r\n\t\t\txml_node cur = first_child();\r\n\r\n\t\t\twhile (cur._root && cur._root != _root)\r\n\t\t\t{\r\n\t\t\t\tif (pred(cur)) return cur;\r\n\r\n\t\t\t\tif (cur.first_child()) cur = cur.first_child();\r\n\t\t\t\telse if (cur.next_sibling()) cur = cur.next_sibling();\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\twhile (!cur.next_sibling() && cur._root != _root) cur = cur.parent();\r\n\r\n\t\t\t\t\tif (cur._root != _root) cur = cur.next_sibling();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\treturn xml_node();\r\n\t\t}\r\n\r\n\t\t// Find child node by attribute name/value\r\n\t\txml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const;\r\n\t\txml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const;\r\n\r\n\t#ifndef PUGIXML_NO_STL\r\n\t\t// Get the absolute node path from root as a text string.\r\n\t\tstring_t path(char_t delimiter = '/') const;\r\n\t#endif\r\n\r\n\t\t// Search for a node by path consisting of node names and . or .. elements.\r\n\t\txml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const;\r\n\r\n\t\t// Recursively traverse subtree with xml_tree_walker\r\n\t\tbool traverse(xml_tree_walker& walker);\r\n\r\n\t#ifndef PUGIXML_NO_XPATH\r\n\t\t// Select single node by evaluating XPath query. Returns first node from the resulting node set.\r\n\t\txpath_node select_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const;\r\n\t\txpath_node select_node(const xpath_query& query) const;\r\n\r\n\t\t// Select node set by evaluating XPath query\r\n\t\txpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const;\r\n\t\txpath_node_set select_nodes(const xpath_query& query) const;\r\n\r\n\t\t// (deprecated: use select_node instead) Select single node by evaluating XPath query.\r\n\t\tPUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const;\r\n\t\tPUGIXML_DEPRECATED xpath_node select_single_node(const xpath_query& query) const;\r\n\r\n\t#endif\r\n\r\n\t\t// Print subtree using a writer object\r\n\t\tvoid print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT(\"\\t\"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;\r\n\r\n\t#ifndef PUGIXML_NO_STL\r\n\t\t// Print subtree to stream\r\n\t\tvoid print(std::basic_ostream<char, std::char_traits<char> >& os, const char_t* indent = PUGIXML_TEXT(\"\\t\"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;\r\n\t\tvoid print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& os, const char_t* indent = PUGIXML_TEXT(\"\\t\"), unsigned int flags = format_default, unsigned int depth = 0) const;\r\n\t#endif\r\n\r\n\t\t// Child nodes iterators\r\n\t\ttypedef xml_node_iterator iterator;\r\n\r\n\t\titerator begin() const;\r\n\t\titerator end() const;\r\n\r\n\t\t// Attribute iterators\r\n\t\ttypedef xml_attribute_iterator attribute_iterator;\r\n\r\n\t\tattribute_iterator attributes_begin() const;\r\n\t\tattribute_iterator attributes_end() const;\r\n\r\n\t\t// Range-based for support\r\n\t\txml_object_range<xml_node_iterator> children() const;\r\n\t\txml_object_range<xml_attribute_iterator> attributes() const;\r\n\r\n\t\t// Range-based for support for all children with the specified name\r\n\t\t// Note: name pointer must have a longer lifetime than the returned object; be careful with passing temporaries!\r\n\t\txml_object_range<xml_named_node_iterator> children(const char_t* name) const;\r\n\r\n\t\t// Get node offset in parsed file/string (in char_t units) for debugging purposes\r\n\t\tptrdiff_t offset_debug() const;\r\n\r\n\t\t// Get hash value (unique for handles to the same object)\r\n\t\tsize_t hash_value() const;\r\n\r\n\t\t// Get internal pointer\r\n\t\txml_node_struct* internal_object() const;\r\n\t};\r\n\r\n#ifdef __BORLANDC__\r\n\t// Borland C++ workaround\r\n\tbool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs);\r\n\tbool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs);\r\n#endif\r\n\r\n\t// A helper for working with text inside PCDATA nodes\r\n\tclass PUGIXML_CLASS xml_text\r\n\t{\r\n\t\tfriend class xml_node;\r\n\r\n\t\txml_node_struct* _root;\r\n\r\n\t\ttypedef void (*unspecified_bool_type)(xml_text***);\r\n\r\n\t\texplicit xml_text(xml_node_struct* root);\r\n\r\n\t\txml_node_struct* _data_new();\r\n\t\txml_node_struct* _data() const;\r\n\r\n\tpublic:\r\n\t\t// Default constructor. Constructs an empty object.\r\n\t\txml_text();\r\n\r\n\t\t// Safe bool conversion operator\r\n\t\toperator unspecified_bool_type() const;\r\n\r\n\t\t// Borland C++ workaround\r\n\t\tbool operator!() const;\r\n\r\n\t\t// Check if text object is empty\r\n\t\tbool empty() const;\r\n\r\n\t\t// Get text, or \"\" if object is empty\r\n\t\tconst char_t* get() const;\r\n\r\n\t\t// Get text, or the default value if object is empty\r\n\t\tconst char_t* as_string(const char_t* def = PUGIXML_TEXT(\"\")) const;\r\n\r\n\t\t// Get text as a number, or the default value if conversion did not succeed or object is empty\r\n\t\tint as_int(int def = 0) const;\r\n\t\tunsigned int as_uint(unsigned int def = 0) const;\r\n\t\tdouble as_double(double def = 0) const;\r\n\t\tfloat as_float(float def = 0) const;\r\n\r\n\t#ifdef PUGIXML_HAS_LONG_LONG\r\n\t\tlong long as_llong(long long def = 0) const;\r\n\t\tunsigned long long as_ullong(unsigned long long def = 0) const;\r\n\t#endif\r\n\r\n\t\t// Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty\r\n\t\tbool as_bool(bool def = false) const;\r\n\r\n\t\t// Set text (returns false if object is empty or there is not enough memory)\r\n\t\tbool set(const char_t* rhs);\r\n\t\tbool set(const char_t* rhs, size_t size);\r\n\r\n\t\t// Set text with type conversion (numbers are converted to strings, boolean is converted to \"true\"/\"false\")\r\n\t\tbool set(int rhs);\r\n\t\tbool set(unsigned int rhs);\r\n\t\tbool set(long rhs);\r\n\t\tbool set(unsigned long rhs);\r\n\t\tbool set(double rhs);\r\n\t\tbool set(double rhs, int precision);\r\n\t\tbool set(float rhs);\r\n\t\tbool set(float rhs, int precision);\r\n\t\tbool set(bool rhs);\r\n\r\n\t#ifdef PUGIXML_HAS_LONG_LONG\r\n\t\tbool set(long long rhs);\r\n\t\tbool set(unsigned long long rhs);\r\n\t#endif\r\n\r\n\t\t// Set text (equivalent to set without error checking)\r\n\t\txml_text& operator=(const char_t* rhs);\r\n\t\txml_text& operator=(int rhs);\r\n\t\txml_text& operator=(unsigned int rhs);\r\n\t\txml_text& operator=(long rhs);\r\n\t\txml_text& operator=(unsigned long rhs);\r\n\t\txml_text& operator=(double rhs);\r\n\t\txml_text& operator=(float rhs);\r\n\t\txml_text& operator=(bool rhs);\r\n\r\n\t#ifdef PUGIXML_HAS_LONG_LONG\r\n\t\txml_text& operator=(long long rhs);\r\n\t\txml_text& operator=(unsigned long long rhs);\r\n\t#endif\r\n\r\n\t\t// Get the data node (node_pcdata or node_cdata) for this object\r\n\t\txml_node data() const;\r\n\t};\r\n\r\n#ifdef __BORLANDC__\r\n\t// Borland C++ workaround\r\n\tbool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs);\r\n\tbool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs);\r\n#endif\r\n\r\n\t// Child node iterator (a bidirectional iterator over a collection of xml_node)\r\n\tclass PUGIXML_CLASS xml_node_iterator\r\n\t{\r\n\t\tfriend class xml_node;\r\n\r\n\tprivate:\r\n\t\tmutable xml_node _wrap;\r\n\t\txml_node _parent;\r\n\r\n\t\txml_node_iterator(xml_node_struct* ref, xml_node_struct* parent);\r\n\r\n\tpublic:\r\n\t\t// Iterator traits\r\n\t\ttypedef ptrdiff_t difference_type;\r\n\t\ttypedef xml_node value_type;\r\n\t\ttypedef xml_node* pointer;\r\n\t\ttypedef xml_node& reference;\r\n\r\n\t#ifndef PUGIXML_NO_STL\r\n\t\ttypedef std::bidirectional_iterator_tag iterator_category;\r\n\t#endif\r\n\r\n\t\t// Default constructor\r\n\t\txml_node_iterator();\r\n\r\n\t\t// Construct an iterator which points to the specified node\r\n\t\txml_node_iterator(const xml_node& node);\r\n\r\n\t\t// Iterator operators\r\n\t\tbool operator==(const xml_node_iterator& rhs) const;\r\n\t\tbool operator!=(const xml_node_iterator& rhs) const;\r\n\r\n\t\txml_node& operator*() const;\r\n\t\txml_node* operator->() const;\r\n\r\n\t\txml_node_iterator& operator++();\r\n\t\txml_node_iterator operator++(int);\r\n\r\n\t\txml_node_iterator& operator--();\r\n\t\txml_node_iterator operator--(int);\r\n\t};\r\n\r\n\t// Attribute iterator (a bidirectional iterator over a collection of xml_attribute)\r\n\tclass PUGIXML_CLASS xml_attribute_iterator\r\n\t{\r\n\t\tfriend class xml_node;\r\n\r\n\tprivate:\r\n\t\tmutable xml_attribute _wrap;\r\n\t\txml_node _parent;\r\n\r\n\t\txml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent);\r\n\r\n\tpublic:\r\n\t\t// Iterator traits\r\n\t\ttypedef ptrdiff_t difference_type;\r\n\t\ttypedef xml_attribute value_type;\r\n\t\ttypedef xml_attribute* pointer;\r\n\t\ttypedef xml_attribute& reference;\r\n\r\n\t#ifndef PUGIXML_NO_STL\r\n\t\ttypedef std::bidirectional_iterator_tag iterator_category;\r\n\t#endif\r\n\r\n\t\t// Default constructor\r\n\t\txml_attribute_iterator();\r\n\r\n\t\t// Construct an iterator which points to the specified attribute\r\n\t\txml_attribute_iterator(const xml_attribute& attr, const xml_node& parent);\r\n\r\n\t\t// Iterator operators\r\n\t\tbool operator==(const xml_attribute_iterator& rhs) const;\r\n\t\tbool operator!=(const xml_attribute_iterator& rhs) const;\r\n\r\n\t\txml_attribute& operator*() const;\r\n\t\txml_attribute* operator->() const;\r\n\r\n\t\txml_attribute_iterator& operator++();\r\n\t\txml_attribute_iterator operator++(int);\r\n\r\n\t\txml_attribute_iterator& operator--();\r\n\t\txml_attribute_iterator operator--(int);\r\n\t};\r\n\r\n\t// Named node range helper\r\n\tclass PUGIXML_CLASS xml_named_node_iterator\r\n\t{\r\n\t\tfriend class xml_node;\r\n\r\n\tpublic:\r\n\t\t// Iterator traits\r\n\t\ttypedef ptrdiff_t difference_type;\r\n\t\ttypedef xml_node value_type;\r\n\t\ttypedef xml_node* pointer;\r\n\t\ttypedef xml_node& reference;\r\n\r\n\t#ifndef PUGIXML_NO_STL\r\n\t\ttypedef std::bidirectional_iterator_tag iterator_category;\r\n\t#endif\r\n\r\n\t\t// Default constructor\r\n\t\txml_named_node_iterator();\r\n\r\n\t\t// Construct an iterator which points to the specified node\r\n\t\t// Note: name pointer is stored in the iterator and must have a longer lifetime than iterator itself\r\n\t\txml_named_node_iterator(const xml_node& node, const char_t* name);\r\n\r\n\t\t// Iterator operators\r\n\t\tbool operator==(const xml_named_node_iterator& rhs) const;\r\n\t\tbool operator!=(const xml_named_node_iterator& rhs) const;\r\n\r\n\t\txml_node& operator*() const;\r\n\t\txml_node* operator->() const;\r\n\r\n\t\txml_named_node_iterator& operator++();\r\n\t\txml_named_node_iterator operator++(int);\r\n\r\n\t\txml_named_node_iterator& operator--();\r\n\t\txml_named_node_iterator operator--(int);\r\n\r\n\tprivate:\r\n\t\tmutable xml_node _wrap;\r\n\t\txml_node _parent;\r\n\t\tconst char_t* _name;\r\n\r\n\t\txml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name);\r\n\t};\r\n\r\n\t// Abstract tree walker class (see xml_node::traverse)\r\n\tclass PUGIXML_CLASS xml_tree_walker\r\n\t{\r\n\t\tfriend class xml_node;\r\n\r\n\tprivate:\r\n\t\tint _depth;\r\n\r\n\tprotected:\r\n\t\t// Get current traversal depth\r\n\t\tint depth() const;\r\n\r\n\tpublic:\r\n\t\txml_tree_walker();\r\n\t\tvirtual ~xml_tree_walker();\r\n\r\n\t\t// Callback that is called when traversal begins\r\n\t\tvirtual bool begin(xml_node& node);\r\n\r\n\t\t// Callback that is called for each node traversed\r\n\t\tvirtual bool for_each(xml_node& node) = 0;\r\n\r\n\t\t// Callback that is called when traversal ends\r\n\t\tvirtual bool end(xml_node& node);\r\n\t};\r\n\r\n\t// Parsing status, returned as part of xml_parse_result object\r\n\tenum xml_parse_status\r\n\t{\r\n\t\tstatus_ok = 0,\t\t\t\t// No error\r\n\r\n\t\tstatus_file_not_found,\t\t// File was not found during load_file()\r\n\t\tstatus_io_error,\t\t\t// Error reading from file/stream\r\n\t\tstatus_out_of_memory,\t\t// Could not allocate memory\r\n\t\tstatus_internal_error,\t\t// Internal error occurred\r\n\r\n\t\tstatus_unrecognized_tag,\t// Parser could not determine tag type\r\n\r\n\t\tstatus_bad_pi,\t\t\t\t// Parsing error occurred while parsing document declaration/processing instruction\r\n\t\tstatus_bad_comment,\t\t\t// Parsing error occurred while parsing comment\r\n\t\tstatus_bad_cdata,\t\t\t// Parsing error occurred while parsing CDATA section\r\n\t\tstatus_bad_doctype,\t\t\t// Parsing error occurred while parsing document type declaration\r\n\t\tstatus_bad_pcdata,\t\t\t// Parsing error occurred while parsing PCDATA section\r\n\t\tstatus_bad_start_element,\t// Parsing error occurred while parsing start element tag\r\n\t\tstatus_bad_attribute,\t\t// Parsing error occurred while parsing element attribute\r\n\t\tstatus_bad_end_element,\t\t// Parsing error occurred while parsing end element tag\r\n\t\tstatus_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag)\r\n\r\n\t\tstatus_append_invalid_root,\t// Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer)\r\n\r\n\t\tstatus_no_document_element\t// Parsing resulted in a document without element nodes\r\n\t};\r\n\r\n\t// Parsing result\r\n\tstruct PUGIXML_CLASS xml_parse_result\r\n\t{\r\n\t\t// Parsing status (see xml_parse_status)\r\n\t\txml_parse_status status;\r\n\r\n\t\t// Last parsed offset (in char_t units from start of input data)\r\n\t\tptrdiff_t offset;\r\n\r\n\t\t// Source document encoding\r\n\t\txml_encoding encoding;\r\n\r\n\t\t// Default constructor, initializes object to failed state\r\n\t\txml_parse_result();\r\n\r\n\t\t// Cast to bool operator\r\n\t\toperator bool() const;\r\n\r\n\t\t// Get error description\r\n\t\tconst char* description() const;\r\n\t};\r\n\r\n\t// Document class (DOM tree root)\r\n\tclass PUGIXML_CLASS xml_document: public xml_node\r\n\t{\r\n\tprivate:\r\n\t\tchar_t* _buffer;\r\n\r\n\t\tchar _memory[192];\r\n\r\n\t\t// Non-copyable semantics\r\n\t\txml_document(const xml_document&);\r\n\t\txml_document& operator=(const xml_document&);\r\n\r\n\t\tvoid _create();\r\n\t\tvoid _destroy();\r\n\t\tvoid _move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT;\r\n\r\n\tpublic:\r\n\t\t// Default constructor, makes empty document\r\n\t\txml_document();\r\n\r\n\t\t// Destructor, invalidates all node/attribute handles to this document\r\n\t\t~xml_document();\r\n\r\n\t#ifdef PUGIXML_HAS_MOVE\r\n\t\t// Move semantics support\r\n\t\txml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT;\r\n\t\txml_document& operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT;\r\n\t#endif\r\n\r\n\t\t// Removes all nodes, leaving the empty document\r\n\t\tvoid reset();\r\n\r\n\t\t// Removes all nodes, then copies the entire contents of the specified document\r\n\t\tvoid reset(const xml_document& proto);\r\n\r\n\t#ifndef PUGIXML_NO_STL\r\n\t\t// Load document from stream.\r\n\t\txml_parse_result load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);\r\n\t\txml_parse_result load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options = parse_default);\r\n\t#endif\r\n\r\n\t\t// (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied.\r\n\t\tPUGIXML_DEPRECATED xml_parse_result load(const char_t* contents, unsigned int options = parse_default);\r\n\r\n\t\t// Load document from zero-terminated string. No encoding conversions are applied.\r\n\t\txml_parse_result load_string(const char_t* contents, unsigned int options = parse_default);\r\n\r\n\t\t// Load document from file\r\n\t\txml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);\r\n\t\txml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);\r\n\r\n\t\t// Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns.\r\n\t\txml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);\r\n\r\n\t\t// Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).\r\n\t\t// You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed.\r\n\t\txml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);\r\n\r\n\t\t// Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).\r\n\t\t// You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore).\r\n\t\txml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);\r\n\r\n\t\t// Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details).\r\n\t\tvoid save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT(\"\\t\"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;\r\n\r\n\t#ifndef PUGIXML_NO_STL\r\n\t\t// Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details).\r\n\t\tvoid save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent = PUGIXML_TEXT(\"\\t\"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;\r\n\t\tvoid save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent = PUGIXML_TEXT(\"\\t\"), unsigned int flags = format_default) const;\r\n\t#endif\r\n\r\n\t\t// Save XML to file\r\n\t\tbool save_file(const char* path, const char_t* indent = PUGIXML_TEXT(\"\\t\"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;\r\n\t\tbool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT(\"\\t\"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;\r\n\r\n\t\t// Get document element\r\n\t\txml_node document_element() const;\r\n\t};\r\n\r\n#ifndef PUGIXML_NO_XPATH\r\n\t// XPath query return type\r\n\tenum xpath_value_type\r\n\t{\r\n\t\txpath_type_none,\t  // Unknown type (query failed to compile)\r\n\t\txpath_type_node_set,  // Node set (xpath_node_set)\r\n\t\txpath_type_number,\t  // Number\r\n\t\txpath_type_string,\t  // String\r\n\t\txpath_type_boolean\t  // Boolean\r\n\t};\r\n\r\n\t// XPath parsing result\r\n\tstruct PUGIXML_CLASS xpath_parse_result\r\n\t{\r\n\t\t// Error message (0 if no error)\r\n\t\tconst char* error;\r\n\r\n\t\t// Last parsed offset (in char_t units from string start)\r\n\t\tptrdiff_t offset;\r\n\r\n\t\t// Default constructor, initializes object to failed state\r\n\t\txpath_parse_result();\r\n\r\n\t\t// Cast to bool operator\r\n\t\toperator bool() const;\r\n\r\n\t\t// Get error description\r\n\t\tconst char* description() const;\r\n\t};\r\n\r\n\t// A single XPath variable\r\n\tclass PUGIXML_CLASS xpath_variable\r\n\t{\r\n\t\tfriend class xpath_variable_set;\r\n\r\n\tprotected:\r\n\t\txpath_value_type _type;\r\n\t\txpath_variable* _next;\r\n\r\n\t\txpath_variable(xpath_value_type type);\r\n\r\n\t\t// Non-copyable semantics\r\n\t\txpath_variable(const xpath_variable&);\r\n\t\txpath_variable& operator=(const xpath_variable&);\r\n\r\n\tpublic:\r\n\t\t// Get variable name\r\n\t\tconst char_t* name() const;\r\n\r\n\t\t// Get variable type\r\n\t\txpath_value_type type() const;\r\n\r\n\t\t// Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error\r\n\t\tbool get_boolean() const;\r\n\t\tdouble get_number() const;\r\n\t\tconst char_t* get_string() const;\r\n\t\tconst xpath_node_set& get_node_set() const;\r\n\r\n\t\t// Set variable value; no type conversion is performed, false is returned on type mismatch error\r\n\t\tbool set(bool value);\r\n\t\tbool set(double value);\r\n\t\tbool set(const char_t* value);\r\n\t\tbool set(const xpath_node_set& value);\r\n\t};\r\n\r\n\t// A set of XPath variables\r\n\tclass PUGIXML_CLASS xpath_variable_set\r\n\t{\r\n\tprivate:\r\n\t\txpath_variable* _data[64];\r\n\r\n\t\tvoid _assign(const xpath_variable_set& rhs);\r\n\t\tvoid _swap(xpath_variable_set& rhs);\r\n\r\n\t\txpath_variable* _find(const char_t* name) const;\r\n\r\n\t\tstatic bool _clone(xpath_variable* var, xpath_variable** out_result);\r\n\t\tstatic void _destroy(xpath_variable* var);\r\n\r\n\tpublic:\r\n\t\t// Default constructor/destructor\r\n\t\txpath_variable_set();\r\n\t\t~xpath_variable_set();\r\n\r\n\t\t// Copy constructor/assignment operator\r\n\t\txpath_variable_set(const xpath_variable_set& rhs);\r\n\t\txpath_variable_set& operator=(const xpath_variable_set& rhs);\r\n\r\n\t#ifdef PUGIXML_HAS_MOVE\r\n\t\t// Move semantics support\r\n\t\txpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT;\r\n\t\txpath_variable_set& operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT;\r\n\t#endif\r\n\r\n\t\t// Add a new variable or get the existing one, if the types match\r\n\t\txpath_variable* add(const char_t* name, xpath_value_type type);\r\n\r\n\t\t// Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch\r\n\t\tbool set(const char_t* name, bool value);\r\n\t\tbool set(const char_t* name, double value);\r\n\t\tbool set(const char_t* name, const char_t* value);\r\n\t\tbool set(const char_t* name, const xpath_node_set& value);\r\n\r\n\t\t// Get existing variable by name\r\n\t\txpath_variable* get(const char_t* name);\r\n\t\tconst xpath_variable* get(const char_t* name) const;\r\n\t};\r\n\r\n\t// A compiled XPath query object\r\n\tclass PUGIXML_CLASS xpath_query\r\n\t{\r\n\tprivate:\r\n\t\tvoid* _impl;\r\n\t\txpath_parse_result _result;\r\n\r\n\t\ttypedef void (*unspecified_bool_type)(xpath_query***);\r\n\r\n\t\t// Non-copyable semantics\r\n\t\txpath_query(const xpath_query&);\r\n\t\txpath_query& operator=(const xpath_query&);\r\n\r\n\tpublic:\r\n\t\t// Construct a compiled object from XPath expression.\r\n\t\t// If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors.\r\n\t\texplicit xpath_query(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL);\r\n\r\n\t\t// Constructor\r\n\t\txpath_query();\r\n\r\n\t\t// Destructor\r\n\t\t~xpath_query();\r\n\r\n\t#ifdef PUGIXML_HAS_MOVE\r\n\t\t// Move semantics support\r\n\t\txpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT;\r\n\t\txpath_query& operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT;\r\n\t#endif\r\n\r\n\t\t// Get query expression return type\r\n\t\txpath_value_type return_type() const;\r\n\r\n\t\t// Evaluate expression as boolean value in the specified context; performs type conversion if necessary.\r\n\t\t// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.\r\n\t\tbool evaluate_boolean(const xpath_node& n) const;\r\n\r\n\t\t// Evaluate expression as double value in the specified context; performs type conversion if necessary.\r\n\t\t// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.\r\n\t\tdouble evaluate_number(const xpath_node& n) const;\r\n\r\n\t#ifndef PUGIXML_NO_STL\r\n\t\t// Evaluate expression as string value in the specified context; performs type conversion if necessary.\r\n\t\t// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.\r\n\t\tstring_t evaluate_string(const xpath_node& n) const;\r\n\t#endif\r\n\r\n\t\t// Evaluate expression as string value in the specified context; performs type conversion if necessary.\r\n\t\t// At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero).\r\n\t\t// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.\r\n\t\t// If PUGIXML_NO_EXCEPTIONS is defined, returns empty  set instead.\r\n\t\tsize_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const;\r\n\r\n\t\t// Evaluate expression as node set in the specified context.\r\n\t\t// If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.\r\n\t\t// If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead.\r\n\t\txpath_node_set evaluate_node_set(const xpath_node& n) const;\r\n\r\n\t\t// Evaluate expression as node set in the specified context.\r\n\t\t// Return first node in document order, or empty node if node set is empty.\r\n\t\t// If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.\r\n\t\t// If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead.\r\n\t\txpath_node evaluate_node(const xpath_node& n) const;\r\n\r\n\t\t// Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode)\r\n\t\tconst xpath_parse_result& result() const;\r\n\r\n\t\t// Safe bool conversion operator\r\n\t\toperator unspecified_bool_type() const;\r\n\r\n\t\t// Borland C++ workaround\r\n\t\tbool operator!() const;\r\n\t};\r\n\r\n\t#ifndef PUGIXML_NO_EXCEPTIONS\r\n        #if defined(_MSC_VER)\r\n          // C4275 can be ignored in Visual C++ if you are deriving\r\n          // from a type in the Standard C++ Library\r\n          #pragma warning(push)\r\n          #pragma warning(disable: 4275)\r\n        #endif\r\n\t// XPath exception class\r\n\tclass PUGIXML_CLASS xpath_exception: public std::exception\r\n\t{\r\n\tprivate:\r\n\t\txpath_parse_result _result;\r\n\r\n\tpublic:\r\n\t\t// Construct exception from parse result\r\n\t\texplicit xpath_exception(const xpath_parse_result& result);\r\n\r\n\t\t// Get error message\r\n\t\tvirtual const char* what() const throw() PUGIXML_OVERRIDE;\r\n\r\n\t\t// Get parse result\r\n\t\tconst xpath_parse_result& result() const;\r\n\t};\r\n        #if defined(_MSC_VER)\r\n          #pragma warning(pop)\r\n        #endif\r\n\t#endif\r\n\r\n\t// XPath node class (either xml_node or xml_attribute)\r\n\tclass PUGIXML_CLASS xpath_node\r\n\t{\r\n\tprivate:\r\n\t\txml_node _node;\r\n\t\txml_attribute _attribute;\r\n\r\n\t\ttypedef void (*unspecified_bool_type)(xpath_node***);\r\n\r\n\tpublic:\r\n\t\t// Default constructor; constructs empty XPath node\r\n\t\txpath_node();\r\n\r\n\t\t// Construct XPath node from XML node/attribute\r\n\t\txpath_node(const xml_node& node);\r\n\t\txpath_node(const xml_attribute& attribute, const xml_node& parent);\r\n\r\n\t\t// Get node/attribute, if any\r\n\t\txml_node node() const;\r\n\t\txml_attribute attribute() const;\r\n\r\n\t\t// Get parent of contained node/attribute\r\n\t\txml_node parent() const;\r\n\r\n\t\t// Safe bool conversion operator\r\n\t\toperator unspecified_bool_type() const;\r\n\r\n\t\t// Borland C++ workaround\r\n\t\tbool operator!() const;\r\n\r\n\t\t// Comparison operators\r\n\t\tbool operator==(const xpath_node& n) const;\r\n\t\tbool operator!=(const xpath_node& n) const;\r\n\t};\r\n\r\n#ifdef __BORLANDC__\r\n\t// Borland C++ workaround\r\n\tbool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs);\r\n\tbool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs);\r\n#endif\r\n\r\n\t// A fixed-size collection of XPath nodes\r\n\tclass PUGIXML_CLASS xpath_node_set\r\n\t{\r\n\tpublic:\r\n\t\t// Collection type\r\n\t\tenum type_t\r\n\t\t{\r\n\t\t\ttype_unsorted,\t\t\t// Not ordered\r\n\t\t\ttype_sorted,\t\t\t// Sorted by document order (ascending)\r\n\t\t\ttype_sorted_reverse\t\t// Sorted by document order (descending)\r\n\t\t};\r\n\r\n\t\t// Constant iterator type\r\n\t\ttypedef const xpath_node* const_iterator;\r\n\r\n\t\t// We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work\r\n\t\ttypedef const xpath_node* iterator;\r\n\r\n\t\t// Default constructor. Constructs empty set.\r\n\t\txpath_node_set();\r\n\r\n\t\t// Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful\r\n\t\txpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted);\r\n\r\n\t\t// Destructor\r\n\t\t~xpath_node_set();\r\n\r\n\t\t// Copy constructor/assignment operator\r\n\t\txpath_node_set(const xpath_node_set& ns);\r\n\t\txpath_node_set& operator=(const xpath_node_set& ns);\r\n\r\n\t#ifdef PUGIXML_HAS_MOVE\r\n\t\t// Move semantics support\r\n\t\txpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT;\r\n\t\txpath_node_set& operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT;\r\n\t#endif\r\n\r\n\t\t// Get collection type\r\n\t\ttype_t type() const;\r\n\r\n\t\t// Get collection size\r\n\t\tsize_t size() const;\r\n\r\n\t\t// Indexing operator\r\n\t\tconst xpath_node& operator[](size_t index) const;\r\n\r\n\t\t// Collection iterators\r\n\t\tconst_iterator begin() const;\r\n\t\tconst_iterator end() const;\r\n\r\n\t\t// Sort the collection in ascending/descending order by document order\r\n\t\tvoid sort(bool reverse = false);\r\n\r\n\t\t// Get first node in the collection by document order\r\n\t\txpath_node first() const;\r\n\r\n\t\t// Check if collection is empty\r\n\t\tbool empty() const;\r\n\r\n\tprivate:\r\n\t\ttype_t _type;\r\n\r\n\t\txpath_node _storage[1];\r\n\r\n\t\txpath_node* _begin;\r\n\t\txpath_node* _end;\r\n\r\n\t\tvoid _assign(const_iterator begin, const_iterator end, type_t type);\r\n\t\tvoid _move(xpath_node_set& rhs) PUGIXML_NOEXCEPT;\r\n\t};\r\n#endif\r\n\r\n#ifndef PUGIXML_NO_STL\r\n\t// Convert wide string to UTF8\r\n\tstd::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const wchar_t* str);\r\n\tstd::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >& str);\r\n\r\n\t// Convert UTF8 to wide string\r\n\tstd::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const char* str);\r\n\tstd::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >& str);\r\n#endif\r\n\r\n\t// Memory allocation function interface; returns pointer to allocated memory or NULL on failure\r\n\ttypedef void* (*allocation_function)(size_t size);\r\n\r\n\t// Memory deallocation function interface\r\n\ttypedef void (*deallocation_function)(void* ptr);\r\n\r\n\t// Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions.\r\n\tvoid PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate);\r\n\r\n\t// Get current memory management functions\r\n\tallocation_function PUGIXML_FUNCTION get_memory_allocation_function();\r\n\tdeallocation_function PUGIXML_FUNCTION get_memory_deallocation_function();\r\n}\r\n\r\n#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))\r\nnamespace std\r\n{\r\n\t// Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)\r\n\tstd::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&);\r\n\tstd::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&);\r\n\tstd::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&);\r\n}\r\n#endif\r\n\r\n#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)\r\nnamespace std\r\n{\r\n\t// Workarounds for (non-standard) iterator category detection\r\n\tstd::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&);\r\n\tstd::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&);\r\n\tstd::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&);\r\n}\r\n#endif\r\n\r\n#endif\r\n\r\n// Make sure implementation is included in header-only mode\r\n// Use macro expansion in #include to work around QMake (QTBUG-11923)\r\n#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE)\r\n#\tdefine PUGIXML_SOURCE \"pugixml.cpp\"\r\n#\tinclude PUGIXML_SOURCE\r\n#endif\r\n\r\n/**\r\n * Copyright (c) 2006-2023 Arseny Kapoulkine\r\n *\r\n * Permission is hereby granted, free of charge, to any person\r\n * obtaining a copy of this software and associated documentation\r\n * files (the \"Software\"), to deal in the Software without\r\n * restriction, including without limitation the rights to use,\r\n * copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n * copies of the Software, and to permit persons to whom the\r\n * Software is furnished to do so, subject to the following\r\n * conditions:\r\n *\r\n * The above copyright notice and this permission notice shall be\r\n * included in all copies or substantial portions of the Software.\r\n *\r\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\r\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\r\n * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\r\n * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\r\n * OTHER DEALINGS IN THE SOFTWARE.\r\n */\r\n"
  }
]